From fb4c6eba439c4dcd3a58caae407d5d49a7d6e96d Mon Sep 17 00:00:00 2001 From: Daniel Jacobowitz Date: Fri, 11 Mar 2005 02:24:23 +0000 Subject: [PATCH] * Makefile.in (SFILES): Add cp-names.y. (libiberty_h, safe_ctype_h): New. (YYFILES): Add cp-names.c. (YYOBJ): Add cp-names.o. (test-cp-names.o, test-cp-names$(EXEEXT), cp-names.o): New rules. (clean): Remove test-cp-names$(EXEEXT). (local-maintainer-clean): Remove cp-names.c. * cp-names.y: New file. * cp-support.c (find_last_component): Delete. (d_left, d_right): Define. (cp_canonicalize_string, mangled_name_to_comp): New functions. (cp_class_name_from_physname, method_name_from_physname): Rewrite to use mangled_name_to_comp. * cp-support.h (cp_canonicalize_string, cp_demangled_name_to_comp) (cp_comp_to_string): New prototypes. * config/djgpp/fnchange.lst: Add cp-names.c. --- gdb/ChangeLog | 21 +- gdb/Makefile.in | 18 +- gdb/config/djgpp/fnchange.lst | 1 + gdb/cp-names.y | 2131 +++++++++++++++++++++++++++++++++ gdb/cp-support.c | 289 +++-- gdb/cp-support.h | 13 +- 6 files changed, 2368 insertions(+), 105 deletions(-) create mode 100644 gdb/cp-names.y diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 3f4196e230..7ed6237dda 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,6 +1,25 @@ +2005-03-10 Daniel Jacobowitz + + * Makefile.in (SFILES): Add cp-names.y. + (libiberty_h, safe_ctype_h): New. + (YYFILES): Add cp-names.c. + (YYOBJ): Add cp-names.o. + (test-cp-names.o, test-cp-names$(EXEEXT), cp-names.o): New rules. + (clean): Remove test-cp-names$(EXEEXT). + (local-maintainer-clean): Remove cp-names.c. + * cp-names.y: New file. + * cp-support.c (find_last_component): Delete. + (d_left, d_right): Define. + (cp_canonicalize_string, mangled_name_to_comp): New functions. + (cp_class_name_from_physname, method_name_from_physname): Rewrite + to use mangled_name_to_comp. + * cp-support.h (cp_canonicalize_string, cp_demangled_name_to_comp) + (cp_comp_to_string): New prototypes. + * config/djgpp/fnchange.lst: Add cp-names.c. + 2005-03-10 Bob Rossi - * main.c(print_gdb_help): remove the --[no]sync help message + * main.c (print_gdb_help): Remove the --[no]async help message. 2005-03-10 Mark Kettenis diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 064dd88d2a..b08aa4df1f 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -519,6 +519,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c \ charset.c cli-out.c coffread.c coff-pe-read.c \ complaints.c completer.c corefile.c \ cp-abi.c cp-support.c cp-namespace.c cp-valprint.c \ + cp-names.y \ dbxread.c demangle.c dictionary.c disasm.c doublest.c dummy-frame.c \ dwarfread.c dwarf2expr.c dwarf2loc.c dwarf2read.c dwarf2-frame.c \ elfread.c environ.c eval.c event-loop.c event-top.c expprint.c \ @@ -579,6 +580,7 @@ elf_arm_h = $(INCLUDE_DIR)/elf/arm.h $(elf_reloc_macros_h) elf_bfd_h = $(BFD_SRC)/elf-bfd.h elf_frv_h = $(INCLUDE_DIR)/elf/frv.h $(elf_reloc_macros_h) libaout_h = $(BFD_SRC)/libaout.h +libiberty_h = $(INCLUDE_DIR)/libiberty.h libbfd_h = $(BFD_SRC)/libbfd.h remote_sim_h = $(INCLUDE_DIR)/gdb/remote-sim.h demangle_h = $(INCLUDE_DIR)/demangle.h @@ -596,6 +598,7 @@ gdb_sim_frv_h = $(INCLUDE_DIR)/gdb/sim-frv.h gdb_sim_ppc_h = $(INCLUDE_DIR)/gdb/sim-ppc.h gdb_sim_sh_h = $(INCLUDE_DIR)/gdb/sim-sh.h splay_tree_h = $(INCLUDE_DIR)/splay-tree.h +safe_ctype_h = $(INCLUDE_DIR)/safe-ctype.h hashtab_h = $(INCLUDE_DIR)/hashtab.h # @@ -944,11 +947,13 @@ SUBDIRS = @subdirs@ # For now, shortcut the "configure GDB for fewer languages" stuff. YYFILES = c-exp.c \ + cp-names.c \ objc-exp.c \ ada-exp.c \ jv-exp.c \ f-exp.c m2-exp.c p-exp.c YYOBJ = c-exp.o \ + cp-names.o \ objc-exp.o \ ada-exp.o \ jv-exp.o \ @@ -1079,6 +1084,14 @@ uninstall-tui: rm -f $(DESTDIR)$(bindir)/$$transformed_name$(EXEEXT) \ $(DESTDIR)$(man1dir)/$$transformed_name.1 +# The C++ name parser can be built standalone for testing. +test-cp-names.o: cp-names.c $(safe_ctype_h) $(libiberty_h) $(demangle_h) + $(CC) -c $(INTERNAL_CFLAGS) -DTEST_CPNAMES \ + -o test-cp-names.o cp-names.c + +test-cp-names$(EXEEXT): test-cp-names.o $(LIBIBERTY) + $(CC) -o test-cp-names$(EXEEXT) test-cp-names.o $(LIBIBERTY) + # We do this by grepping through sources. If that turns out to be too slow, # maybe we could just require every .o file to have an initialization routine # of a given name (top.o -> _initialize_top, etc.). @@ -1233,6 +1246,8 @@ clean mostlyclean: $(CONFIG_CLEAN) rm -f init.c version.c rm -f gdb$(EXEEXT) core make.log rm -f gdb[0-9]$(EXEEXT) + rm -f test-cp-names$(EXEEXT) + .PHONY: clean-tui clean-tui: rm -f $(TUI)$(EXEEXT) @@ -1260,6 +1275,7 @@ local-maintainer-clean: @echo "This command is intended for maintainers to use;" @echo "it deletes files that may require special tools to rebuild." rm -f c-exp.c \ + cp-names.c \ ada-lex.c ada-exp.c \ objc-exp.c \ jv-exp.tab \ @@ -1492,7 +1508,6 @@ v850ice.o: $(srcdir)/v850ice.c $(GDBTK_CFLAGS) \ $(srcdir)/v850ice.c - # Message files. Based on code in gcc/Makefile.in. # Rules for generating translated message descriptions. Disabled by @@ -1811,6 +1826,7 @@ core-regset.o: core-regset.c $(defs_h) $(command_h) $(gdbcore_h) \ $(inferior_h) $(target_h) $(gdb_string_h) $(gregset_h) cp-abi.o: cp-abi.c $(defs_h) $(value_h) $(cp_abi_h) $(command_h) $(gdbcmd_h) \ $(ui_out_h) $(gdb_string_h) +cp-names.o: cp-names.c $(safe_ctype_h) $(libiberty_h) $(demangle_h) cp-namespace.o: cp-namespace.c $(defs_h) $(cp_support_h) $(gdb_obstack_h) \ $(symtab_h) $(symfile_h) $(gdb_assert_h) $(block_h) $(objfiles_h) \ $(gdbtypes_h) $(dictionary_h) $(command_h) $(frame_h) diff --git a/gdb/config/djgpp/fnchange.lst b/gdb/config/djgpp/fnchange.lst index fe0a50940d..70f42e23ce 100644 --- a/gdb/config/djgpp/fnchange.lst +++ b/gdb/config/djgpp/fnchange.lst @@ -103,6 +103,7 @@ @V@/gdb/config/rs6000/tm-rs6000.h @V@/gdb/config/rs6000/tm-rs6k.h @V@/gdb/config/rs6000/tm-rs6000ly.h @V@/gdb/config/rs6000/tm-rs6kly.h @V@/gdb/config/sparc/tm-sparclynx.h @V@/gdb/config/sparc/tm-splynx.h +@V@/gdb/cp-names.c @V@/gdb/cpnames.c @V@/gdb/f-exp.tab.c @V@/gdb/f-exp_tab.c @V@/gdb/gdbtk/ChangeLog-2001 @V@/gdb/gdbtk/ChangeLog.001 @V@/gdb/gdbtk/ChangeLog-2002 @V@/gdb/gdbtk/ChangeLog.002 diff --git a/gdb/cp-names.y b/gdb/cp-names.y new file mode 100644 index 0000000000..589d8d90a7 --- /dev/null +++ b/gdb/cp-names.y @@ -0,0 +1,2131 @@ +/* YACC parser for C++ names, for GDB. + + Copyright 2003, 2004, 2005 + Free Software Foundation, Inc. + + Parts of the lexer are based on c-exp.y from GDB. + +This file is part of GDB. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* Note that malloc's and realloc's in this file are transformed to + xmalloc and xrealloc respectively by the same sed command in the + makefile that remaps any other malloc/realloc inserted by the parser + generator. Doing this with #defines and trying to control the interaction + with include files ( and for example) just became + too messy, particularly when such includes can be inserted at random + times by the parser generator. */ + +%{ + +#include +#include +#include +#include + +#include "safe-ctype.h" +#include "libiberty.h" +#include "demangle.h" + +/* Bison does not make it easy to create a parser without global + state, unfortunately. Here are all the global variables used + in this parser. */ + +/* LEXPTR is the current pointer into our lex buffer. PREV_LEXPTR + is the start of the last token lexed, only used for diagnostics. + ERROR_LEXPTR is the first place an error occurred. GLOBAL_ERRMSG + is the first error message encountered. */ + +static const char *lexptr, *prev_lexptr, *error_lexptr, *global_errmsg; + +/* The components built by the parser are allocated ahead of time, + and cached in this structure. */ + +struct demangle_info { + int used; + struct demangle_component comps[1]; +}; + +static struct demangle_info *demangle_info; +#define d_grab() (&demangle_info->comps[demangle_info->used++]) + +/* The parse tree created by the parser is stored here after a successful + parse. */ + +static struct demangle_component *global_result; + +/* Prototypes for helper functions used when constructing the parse + tree. */ + +static struct demangle_component *d_qualify (struct demangle_component *, int, + int); + +static struct demangle_component *d_int_type (int); + +static struct demangle_component *d_unary (const char *, + struct demangle_component *); +static struct demangle_component *d_binary (const char *, + struct demangle_component *, + struct demangle_component *); + +/* Flags passed to d_qualify. */ + +#define QUAL_CONST 1 +#define QUAL_RESTRICT 2 +#define QUAL_VOLATILE 4 + +/* Flags passed to d_int_type. */ + +#define INT_CHAR (1 << 0) +#define INT_SHORT (1 << 1) +#define INT_LONG (1 << 2) +#define INT_LLONG (1 << 3) + +#define INT_SIGNED (1 << 4) +#define INT_UNSIGNED (1 << 5) + +/* Remap normal yacc parser interface names (yyparse, yylex, yyerror, etc), + as well as gratuitiously global symbol names, so we can have multiple + yacc generated parsers in gdb. Note that these are only the variables + produced by yacc. If other parser generators (bison, byacc, etc) produce + additional global names that conflict at link time, then those parser + generators need to be fixed instead of adding those names to this list. */ + +#define yymaxdepth cpname_maxdepth +#define yyparse cpname_parse +#define yylex cpname_lex +#define yyerror cpname_error +#define yylval cpname_lval +#define yychar cpname_char +#define yydebug cpname_debug +#define yypact cpname_pact +#define yyr1 cpname_r1 +#define yyr2 cpname_r2 +#define yydef cpname_def +#define yychk cpname_chk +#define yypgo cpname_pgo +#define yyact cpname_act +#define yyexca cpname_exca +#define yyerrflag cpname_errflag +#define yynerrs cpname_nerrs +#define yyps cpname_ps +#define yypv cpname_pv +#define yys cpname_s +#define yy_yys cpname_yys +#define yystate cpname_state +#define yytmp cpname_tmp +#define yyv cpname_v +#define yy_yyv cpname_yyv +#define yyval cpname_val +#define yylloc cpname_lloc +#define yyreds cpname_reds /* With YYDEBUG defined */ +#define yytoks cpname_toks /* With YYDEBUG defined */ +#define yyname cpname_name /* With YYDEBUG defined */ +#define yyrule cpname_rule /* With YYDEBUG defined */ +#define yylhs cpname_yylhs +#define yylen cpname_yylen +#define yydefred cpname_yydefred +#define yydgoto cpname_yydgoto +#define yysindex cpname_yysindex +#define yyrindex cpname_yyrindex +#define yygindex cpname_yygindex +#define yytable cpname_yytable +#define yycheck cpname_yycheck + +static int yylex (void); +static void yyerror (char *); + +/* Enable yydebug for the stand-alone parser. */ +#ifdef TEST_CPNAMES +# define YYDEBUG 1 +#endif + +/* Helper functions. These wrap the demangler tree interface, handle + allocation from our global store, and return the allocated component. */ + +static struct demangle_component * +fill_comp (enum demangle_component_type d_type, struct demangle_component *lhs, + struct demangle_component *rhs) +{ + struct demangle_component *ret = d_grab (); + cplus_demangle_fill_component (ret, d_type, lhs, rhs); + return ret; +} + +static struct demangle_component * +make_empty (enum demangle_component_type d_type) +{ + struct demangle_component *ret = d_grab (); + ret->type = d_type; + return ret; +} + +static struct demangle_component * +make_operator (const char *name, int args) +{ + struct demangle_component *ret = d_grab (); + cplus_demangle_fill_operator (ret, name, args); + return ret; +} + +static struct demangle_component * +make_dtor (enum gnu_v3_dtor_kinds kind, struct demangle_component *name) +{ + struct demangle_component *ret = d_grab (); + cplus_demangle_fill_dtor (ret, kind, name); + return ret; +} + +static struct demangle_component * +make_builtin_type (const char *name) +{ + struct demangle_component *ret = d_grab (); + cplus_demangle_fill_builtin_type (ret, name); + return ret; +} + +static struct demangle_component * +make_name (const char *name, int len) +{ + struct demangle_component *ret = d_grab (); + cplus_demangle_fill_name (ret, name, len); + return ret; +} + +#define d_left(dc) (dc)->u.s_binary.left +#define d_right(dc) (dc)->u.s_binary.right + +%} + +%union + { + struct demangle_component *comp; + struct nested { + struct demangle_component *comp; + struct demangle_component **last; + } nested; + struct { + struct demangle_component *comp, *last; + } nested1; + struct { + struct demangle_component *comp, **last; + struct nested fn; + struct demangle_component *start; + int fold_flag; + } abstract; + int lval; + struct { + int val; + struct demangle_component *type; + } typed_val_int; + const char *opname; + } + +%type exp exp1 type start start_opt operator colon_name +%type unqualified_name colon_ext_name +%type template template_arg +%type builtin_type +%type typespec_2 array_indicator +%type colon_ext_only ext_only_name + +%type demangler_special function conversion_op +%type conversion_op_name + +%type abstract_declarator direct_abstract_declarator +%type abstract_declarator_fn +%type declarator direct_declarator function_arglist + +%type declarator_1 direct_declarator_1 + +%type template_params function_args +%type ptr_operator + +%type nested_name + +%type qualifier qualifiers qualifiers_opt + +%type int_part int_seq + +%token INT +%token FLOAT + +%token NAME +%type name + +%token STRUCT CLASS UNION ENUM SIZEOF UNSIGNED COLONCOLON +%token TEMPLATE +%token ERROR +%token NEW DELETE OPERATOR +%token STATIC_CAST REINTERPRET_CAST DYNAMIC_CAST + +/* Special type cases, put in to allow the parser to distinguish different + legal basetypes. */ +%token SIGNED_KEYWORD LONG SHORT INT_KEYWORD CONST_KEYWORD VOLATILE_KEYWORD DOUBLE_KEYWORD BOOL +%token ELLIPSIS RESTRICT VOID FLOAT_KEYWORD CHAR WCHAR_T + +%token ASSIGN_MODIFY + +/* C++ */ +%token TRUEKEYWORD +%token FALSEKEYWORD + +/* Non-C++ things we get from the demangler. */ +%token DEMANGLER_SPECIAL +%token CONSTRUCTION_VTABLE CONSTRUCTION_IN +%token GLOBAL + +%{ +enum { + GLOBAL_CONSTRUCTORS = DEMANGLE_COMPONENT_LITERAL + 20, + GLOBAL_DESTRUCTORS = DEMANGLE_COMPONENT_LITERAL + 21 +}; +%} + +/* Precedence declarations. */ + +/* Give NAME lower precedence than COLONCOLON, so that nested_name will + associate greedily. */ +%nonassoc NAME + +/* Give NEW and DELETE lower precedence than ']', because we can not + have an array of type operator new. This causes NEW '[' to be + parsed as operator new[]. */ +%nonassoc NEW DELETE + +/* Give VOID higher precedence than NAME. Then we can use %prec NAME + to prefer (VOID) to (function_args). */ +%nonassoc VOID + +/* Give VOID lower precedence than ')' for similar reasons. */ +%nonassoc ')' + +%left ',' +%right '=' ASSIGN_MODIFY +%right '?' +%left OROR +%left ANDAND +%left '|' +%left '^' +%left '&' +%left EQUAL NOTEQUAL +%left '<' '>' LEQ GEQ +%left LSH RSH +%left '@' +%left '+' '-' +%left '*' '/' '%' +%right UNARY INCREMENT DECREMENT + +/* We don't need a precedence for '(' in this reduced grammar, and it + can mask some unpleasant bugs, so disable it for now. */ + +%right ARROW '.' '[' /* '(' */ +%left COLONCOLON + + +%% + +result : start + { global_result = $1; } + ; + +start : type + + | demangler_special + + | function + + ; + +start_opt : /* */ + { $$ = NULL; } + | COLONCOLON start + { $$ = $2; } + ; + +function + /* Function with a return type. declarator_1 is used to prevent + ambiguity with the next rule. */ + : typespec_2 declarator_1 + { $$ = $2.comp; + *$2.last = $1; + } + + /* Function without a return type. We need to use typespec_2 + to prevent conflicts from qualifiers_opt - harmless. The + start_opt is used to handle "function-local" variables and + types. */ + | typespec_2 function_arglist start_opt + { $$ = fill_comp (DEMANGLE_COMPONENT_TYPED_NAME, $1, $2.comp); + if ($3) $$ = fill_comp (DEMANGLE_COMPONENT_LOCAL_NAME, $$, $3); } + | colon_ext_only function_arglist start_opt + { $$ = fill_comp (DEMANGLE_COMPONENT_TYPED_NAME, $1, $2.comp); + if ($3) $$ = fill_comp (DEMANGLE_COMPONENT_LOCAL_NAME, $$, $3); } + + | conversion_op_name start_opt + { $$ = $1.comp; + if ($2) $$ = fill_comp (DEMANGLE_COMPONENT_LOCAL_NAME, $$, $2); } + | conversion_op_name abstract_declarator_fn + { if ($2.last) + { + /* First complete the abstract_declarator's type using + the typespec from the conversion_op_name. */ + *$2.last = *$1.last; + /* Then complete the conversion_op_name with the type. */ + *$1.last = $2.comp; + } + /* If we have an arglist, build a function type. */ + if ($2.fn.comp) + $$ = fill_comp (DEMANGLE_COMPONENT_TYPED_NAME, $1.comp, $2.fn.comp); + else + $$ = $1.comp; + if ($2.start) $$ = fill_comp (DEMANGLE_COMPONENT_LOCAL_NAME, $$, $2.start); + } + ; + +demangler_special + : DEMANGLER_SPECIAL start + { $$ = make_empty ($1); + d_left ($$) = $2; + d_right ($$) = NULL; } + | CONSTRUCTION_VTABLE start CONSTRUCTION_IN start + { $$ = fill_comp (DEMANGLE_COMPONENT_CONSTRUCTION_VTABLE, $2, $4); } + | GLOBAL + { $$ = make_empty ($1.val); + d_left ($$) = $1.type; + d_right ($$) = NULL; } + ; + +operator : OPERATOR NEW + { $$ = make_operator ("new", 1); } + | OPERATOR DELETE + { $$ = make_operator ("delete", 1); } + | OPERATOR NEW '[' ']' + { $$ = make_operator ("new[]", 1); } + | OPERATOR DELETE '[' ']' + { $$ = make_operator ("delete[]", 1); } + | OPERATOR '+' + { $$ = make_operator ("+", 2); } + | OPERATOR '-' + { $$ = make_operator ("-", 2); } + | OPERATOR '*' + { $$ = make_operator ("*", 2); } + | OPERATOR '/' + { $$ = make_operator ("/", 2); } + | OPERATOR '%' + { $$ = make_operator ("%", 2); } + | OPERATOR '^' + { $$ = make_operator ("^", 2); } + | OPERATOR '&' + { $$ = make_operator ("&", 2); } + | OPERATOR '|' + { $$ = make_operator ("|", 2); } + | OPERATOR '~' + { $$ = make_operator ("~", 1); } + | OPERATOR '!' + { $$ = make_operator ("!", 1); } + | OPERATOR '=' + { $$ = make_operator ("=", 2); } + | OPERATOR '<' + { $$ = make_operator ("<", 2); } + | OPERATOR '>' + { $$ = make_operator (">", 2); } + | OPERATOR ASSIGN_MODIFY + { $$ = make_operator ($2, 2); } + | OPERATOR LSH + { $$ = make_operator ("<<", 2); } + | OPERATOR RSH + { $$ = make_operator (">>", 2); } + | OPERATOR EQUAL + { $$ = make_operator ("==", 2); } + | OPERATOR NOTEQUAL + { $$ = make_operator ("!=", 2); } + | OPERATOR LEQ + { $$ = make_operator ("<=", 2); } + | OPERATOR GEQ + { $$ = make_operator (">=", 2); } + | OPERATOR ANDAND + { $$ = make_operator ("&&", 2); } + | OPERATOR OROR + { $$ = make_operator ("||", 2); } + | OPERATOR INCREMENT + { $$ = make_operator ("++", 1); } + | OPERATOR DECREMENT + { $$ = make_operator ("--", 1); } + | OPERATOR ',' + { $$ = make_operator (",", 2); } + | OPERATOR ARROW '*' + { $$ = make_operator ("->*", 2); } + | OPERATOR ARROW + { $$ = make_operator ("->", 2); } + | OPERATOR '(' ')' + { $$ = make_operator ("()", 0); } + | OPERATOR '[' ']' + { $$ = make_operator ("[]", 2); } + ; + + /* Conversion operators. We don't try to handle some of + the wackier demangler output for function pointers, + since it's not clear that it's parseable. */ +conversion_op + : OPERATOR typespec_2 + { $$ = fill_comp (DEMANGLE_COMPONENT_CAST, $2, NULL); } + ; + +conversion_op_name + : nested_name conversion_op + { $$.comp = $1.comp; + d_right ($1.last) = $2; + $$.last = &d_left ($2); + } + | conversion_op + { $$.comp = $1; + $$.last = &d_left ($1); + } + | COLONCOLON nested_name conversion_op + { $$.comp = $2.comp; + d_right ($2.last) = $3; + $$.last = &d_left ($3); + } + | COLONCOLON conversion_op + { $$.comp = $2; + $$.last = &d_left ($2); + } + ; + +/* DEMANGLE_COMPONENT_NAME */ +/* This accepts certain invalid placements of '~'. */ +unqualified_name: operator + | operator '<' template_params '>' + { $$ = fill_comp (DEMANGLE_COMPONENT_TEMPLATE, $1, $3.comp); } + | '~' NAME + { $$ = make_dtor (gnu_v3_complete_object_dtor, $2); } + ; + +/* This rule is used in name and nested_name, and expanded inline there + for efficiency. */ +/* +scope_id : NAME + | template + ; +*/ + +colon_name : name + | COLONCOLON name + { $$ = $2; } + ; + +/* DEMANGLE_COMPONENT_QUAL_NAME */ +/* DEMANGLE_COMPONENT_CTOR / DEMANGLE_COMPONENT_DTOR ? */ +name : nested_name NAME %prec NAME + { $$ = $1.comp; d_right ($1.last) = $2; } + | NAME %prec NAME + | nested_name template %prec NAME + { $$ = $1.comp; d_right ($1.last) = $2; } + | template %prec NAME + ; + +colon_ext_name : colon_name + | colon_ext_only + ; + +colon_ext_only : ext_only_name + | COLONCOLON ext_only_name + { $$ = $2; } + ; + +ext_only_name : nested_name unqualified_name + { $$ = $1.comp; d_right ($1.last) = $2; } + | unqualified_name + ; + +nested_name : NAME COLONCOLON + { $$.comp = make_empty (DEMANGLE_COMPONENT_QUAL_NAME); + d_left ($$.comp) = $1; + d_right ($$.comp) = NULL; + $$.last = $$.comp; + } + | nested_name NAME COLONCOLON + { $$.comp = $1.comp; + d_right ($1.last) = make_empty (DEMANGLE_COMPONENT_QUAL_NAME); + $$.last = d_right ($1.last); + d_left ($$.last) = $2; + d_right ($$.last) = NULL; + } + | template COLONCOLON + { $$.comp = make_empty (DEMANGLE_COMPONENT_QUAL_NAME); + d_left ($$.comp) = $1; + d_right ($$.comp) = NULL; + $$.last = $$.comp; + } + | nested_name template COLONCOLON + { $$.comp = $1.comp; + d_right ($1.last) = make_empty (DEMANGLE_COMPONENT_QUAL_NAME); + $$.last = d_right ($1.last); + d_left ($$.last) = $2; + d_right ($$.last) = NULL; + } + ; + +/* DEMANGLE_COMPONENT_TEMPLATE */ +/* DEMANGLE_COMPONENT_TEMPLATE_ARGLIST */ +template : NAME '<' template_params '>' + { $$ = fill_comp (DEMANGLE_COMPONENT_TEMPLATE, $1, $3.comp); } + ; + +template_params : template_arg + { $$.comp = fill_comp (DEMANGLE_COMPONENT_TEMPLATE_ARGLIST, $1, NULL); + $$.last = &d_right ($$.comp); } + | template_params ',' template_arg + { $$.comp = $1.comp; + *$1.last = fill_comp (DEMANGLE_COMPONENT_TEMPLATE_ARGLIST, $3, NULL); + $$.last = &d_right (*$1.last); + } + ; + +/* "type" is inlined into template_arg and function_args. */ + +/* Also an integral constant-expression of integral type, and a + pointer to member (?) */ +template_arg : typespec_2 + | typespec_2 abstract_declarator + { $$ = $2.comp; + *$2.last = $1; + } + | '&' start + { $$ = fill_comp (DEMANGLE_COMPONENT_UNARY, make_operator ("&", 1), $2); } + | '&' '(' start ')' + { $$ = fill_comp (DEMANGLE_COMPONENT_UNARY, make_operator ("&", 1), $3); } + | exp + ; + +function_args : typespec_2 + { $$.comp = fill_comp (DEMANGLE_COMPONENT_ARGLIST, $1, NULL); + $$.last = &d_right ($$.comp); + } + | typespec_2 abstract_declarator + { *$2.last = $1; + $$.comp = fill_comp (DEMANGLE_COMPONENT_ARGLIST, $2.comp, NULL); + $$.last = &d_right ($$.comp); + } + | function_args ',' typespec_2 + { *$1.last = fill_comp (DEMANGLE_COMPONENT_ARGLIST, $3, NULL); + $$.comp = $1.comp; + $$.last = &d_right (*$1.last); + } + | function_args ',' typespec_2 abstract_declarator + { *$4.last = $3; + *$1.last = fill_comp (DEMANGLE_COMPONENT_ARGLIST, $4.comp, NULL); + $$.comp = $1.comp; + $$.last = &d_right (*$1.last); + } + | function_args ',' ELLIPSIS + { *$1.last + = fill_comp (DEMANGLE_COMPONENT_ARGLIST, + make_builtin_type ("..."), + NULL); + $$.comp = $1.comp; + $$.last = &d_right (*$1.last); + } + ; + +function_arglist: '(' function_args ')' qualifiers_opt %prec NAME + { $$.comp = fill_comp (DEMANGLE_COMPONENT_FUNCTION_TYPE, NULL, $2.comp); + $$.last = &d_left ($$.comp); + $$.comp = d_qualify ($$.comp, $4, 1); } + | '(' VOID ')' qualifiers_opt + { $$.comp = fill_comp (DEMANGLE_COMPONENT_FUNCTION_TYPE, NULL, NULL); + $$.last = &d_left ($$.comp); + $$.comp = d_qualify ($$.comp, $4, 1); } + | '(' ')' qualifiers_opt + { $$.comp = fill_comp (DEMANGLE_COMPONENT_FUNCTION_TYPE, NULL, NULL); + $$.last = &d_left ($$.comp); + $$.comp = d_qualify ($$.comp, $3, 1); } + ; + +/* Should do something about DEMANGLE_COMPONENT_VENDOR_TYPE_QUAL */ +qualifiers_opt : /* epsilon */ + { $$ = 0; } + | qualifiers + ; + +qualifier : RESTRICT + { $$ = QUAL_RESTRICT; } + | VOLATILE_KEYWORD + { $$ = QUAL_VOLATILE; } + | CONST_KEYWORD + { $$ = QUAL_CONST; } + ; + +qualifiers : qualifier + | qualifier qualifiers + { $$ = $1 | $2; } + ; + +/* This accepts all sorts of invalid constructions and produces + invalid output for them - an error would be better. */ + +int_part : INT_KEYWORD + { $$ = 0; } + | SIGNED_KEYWORD + { $$ = INT_SIGNED; } + | UNSIGNED + { $$ = INT_UNSIGNED; } + | CHAR + { $$ = INT_CHAR; } + | LONG + { $$ = INT_LONG; } + | SHORT + { $$ = INT_SHORT; } + ; + +int_seq : int_part + | int_seq int_part + { $$ = $1 | $2; if ($1 & $2 & INT_LONG) $$ = $1 | INT_LLONG; } + ; + +builtin_type : int_seq + { $$ = d_int_type ($1); } + | FLOAT_KEYWORD + { $$ = make_builtin_type ("float"); } + | DOUBLE_KEYWORD + { $$ = make_builtin_type ("double"); } + | LONG DOUBLE_KEYWORD + { $$ = make_builtin_type ("long double"); } + | BOOL + { $$ = make_builtin_type ("bool"); } + | WCHAR_T + { $$ = make_builtin_type ("wchar_t"); } + | VOID + { $$ = make_builtin_type ("void"); } + ; + +ptr_operator : '*' qualifiers_opt + { $$.comp = make_empty (DEMANGLE_COMPONENT_POINTER); + $$.comp->u.s_binary.left = $$.comp->u.s_binary.right = NULL; + $$.last = &d_left ($$.comp); + $$.comp = d_qualify ($$.comp, $2, 0); } + /* g++ seems to allow qualifiers after the reference? */ + | '&' + { $$.comp = make_empty (DEMANGLE_COMPONENT_REFERENCE); + $$.comp->u.s_binary.left = $$.comp->u.s_binary.right = NULL; + $$.last = &d_left ($$.comp); } + | nested_name '*' qualifiers_opt + { $$.comp = make_empty (DEMANGLE_COMPONENT_PTRMEM_TYPE); + $$.comp->u.s_binary.left = $1.comp; + /* Convert the innermost DEMANGLE_COMPONENT_QUAL_NAME to a DEMANGLE_COMPONENT_NAME. */ + *$1.last = *d_left ($1.last); + $$.comp->u.s_binary.right = NULL; + $$.last = &d_right ($$.comp); + $$.comp = d_qualify ($$.comp, $3, 0); } + | COLONCOLON nested_name '*' qualifiers_opt + { $$.comp = make_empty (DEMANGLE_COMPONENT_PTRMEM_TYPE); + $$.comp->u.s_binary.left = $2.comp; + /* Convert the innermost DEMANGLE_COMPONENT_QUAL_NAME to a DEMANGLE_COMPONENT_NAME. */ + *$2.last = *d_left ($2.last); + $$.comp->u.s_binary.right = NULL; + $$.last = &d_right ($$.comp); + $$.comp = d_qualify ($$.comp, $4, 0); } + ; + +array_indicator : '[' ']' + { $$ = make_empty (DEMANGLE_COMPONENT_ARRAY_TYPE); + d_left ($$) = NULL; + } + | '[' INT ']' + { $$ = make_empty (DEMANGLE_COMPONENT_ARRAY_TYPE); + d_left ($$) = $2; + } + ; + +/* Details of this approach inspired by the G++ < 3.4 parser. */ + +/* This rule is only used in typespec_2, and expanded inline there for + efficiency. */ +/* +typespec : builtin_type + | colon_name + ; +*/ + +typespec_2 : builtin_type qualifiers + { $$ = d_qualify ($1, $2, 0); } + | builtin_type + | qualifiers builtin_type qualifiers + { $$ = d_qualify ($2, $1 | $3, 0); } + | qualifiers builtin_type + { $$ = d_qualify ($2, $1, 0); } + + | name qualifiers + { $$ = d_qualify ($1, $2, 0); } + | name + | qualifiers name qualifiers + { $$ = d_qualify ($2, $1 | $3, 0); } + | qualifiers name + { $$ = d_qualify ($2, $1, 0); } + + | COLONCOLON name qualifiers + { $$ = d_qualify ($2, $3, 0); } + | COLONCOLON name + { $$ = $2; } + | qualifiers COLONCOLON name qualifiers + { $$ = d_qualify ($3, $1 | $4, 0); } + | qualifiers COLONCOLON name + { $$ = d_qualify ($3, $1, 0); } + ; + +abstract_declarator + : ptr_operator + { $$.comp = $1.comp; $$.last = $1.last; + $$.fn.comp = NULL; $$.fn.last = NULL; } + | ptr_operator abstract_declarator + { $$ = $2; $$.fn.comp = NULL; $$.fn.last = NULL; + if ($2.fn.comp) { $$.last = $2.fn.last; *$2.last = $2.fn.comp; } + *$$.last = $1.comp; + $$.last = $1.last; } + | direct_abstract_declarator + { $$.fn.comp = NULL; $$.fn.last = NULL; + if ($1.fn.comp) { $$.last = $1.fn.last; *$1.last = $1.fn.comp; } + } + ; + +direct_abstract_declarator + : '(' abstract_declarator ')' + { $$ = $2; $$.fn.comp = NULL; $$.fn.last = NULL; $$.fold_flag = 1; + if ($2.fn.comp) { $$.last = $2.fn.last; *$2.last = $2.fn.comp; } + } + | direct_abstract_declarator function_arglist + { $$.fold_flag = 0; + if ($1.fn.comp) { $$.last = $1.fn.last; *$1.last = $1.fn.comp; } + if ($1.fold_flag) + { + *$$.last = $2.comp; + $$.last = $2.last; + } + else + $$.fn = $2; + } + | direct_abstract_declarator array_indicator + { $$.fn.comp = NULL; $$.fn.last = NULL; $$.fold_flag = 0; + if ($1.fn.comp) { $$.last = $1.fn.last; *$1.last = $1.fn.comp; } + *$1.last = $2; + $$.last = &d_right ($2); + } + | array_indicator + { $$.fn.comp = NULL; $$.fn.last = NULL; $$.fold_flag = 0; + $$.comp = $1; + $$.last = &d_right ($1); + } + /* G++ has the following except for () and (type). Then + (type) is handled in regcast_or_absdcl and () is handled + in fcast_or_absdcl. + + However, this is only useful for function types, and + generates reduce/reduce conflicts with direct_declarator. + We're interested in pointer-to-function types, and in + functions, but not in function types - so leave this + out. */ + /* | function_arglist */ + ; + +abstract_declarator_fn + : ptr_operator + { $$.comp = $1.comp; $$.last = $1.last; + $$.fn.comp = NULL; $$.fn.last = NULL; $$.start = NULL; } + | ptr_operator abstract_declarator_fn + { $$ = $2; + if ($2.last) + *$$.last = $1.comp; + else + $$.comp = $1.comp; + $$.last = $1.last; + } + | direct_abstract_declarator + { $$.comp = $1.comp; $$.last = $1.last; $$.fn = $1.fn; $$.start = NULL; } + | direct_abstract_declarator function_arglist COLONCOLON start + { $$.start = $4; + if ($1.fn.comp) { $$.last = $1.fn.last; *$1.last = $1.fn.comp; } + if ($1.fold_flag) + { + *$$.last = $2.comp; + $$.last = $2.last; + } + else + $$.fn = $2; + } + | function_arglist start_opt + { $$.fn = $1; + $$.start = $2; + $$.comp = NULL; $$.last = NULL; + } + ; + +type : typespec_2 + | typespec_2 abstract_declarator + { $$ = $2.comp; + *$2.last = $1; + } + ; + +declarator : ptr_operator declarator + { $$.comp = $2.comp; + $$.last = $1.last; + *$2.last = $1.comp; } + | direct_declarator + ; + +direct_declarator + : '(' declarator ')' + { $$ = $2; } + | direct_declarator function_arglist + { $$.comp = $1.comp; + *$1.last = $2.comp; + $$.last = $2.last; + } + | direct_declarator array_indicator + { $$.comp = $1.comp; + *$1.last = $2; + $$.last = &d_right ($2); + } + | colon_ext_name + { $$.comp = make_empty (DEMANGLE_COMPONENT_TYPED_NAME); + d_left ($$.comp) = $1; + $$.last = &d_right ($$.comp); + } + ; + +/* These are similar to declarator and direct_declarator except that they + do not permit ( colon_ext_name ), which is ambiguous with a function + argument list. They also don't permit a few other forms with redundant + parentheses around the colon_ext_name; any colon_ext_name in parentheses + must be followed by an argument list or an array indicator, or preceded + by a pointer. */ +declarator_1 : ptr_operator declarator_1 + { $$.comp = $2.comp; + $$.last = $1.last; + *$2.last = $1.comp; } + | colon_ext_name + { $$.comp = make_empty (DEMANGLE_COMPONENT_TYPED_NAME); + d_left ($$.comp) = $1; + $$.last = &d_right ($$.comp); + } + | direct_declarator_1 + + /* Function local variable or type. The typespec to + our left is the type of the containing function. + This should be OK, because function local types + can not be templates, so the return types of their + members will not be mangled. If they are hopefully + they'll end up to the right of the ::. */ + | colon_ext_name function_arglist COLONCOLON start + { $$.comp = fill_comp (DEMANGLE_COMPONENT_TYPED_NAME, $1, $2.comp); + $$.last = $2.last; + $$.comp = fill_comp (DEMANGLE_COMPONENT_LOCAL_NAME, $$.comp, $4); + } + | direct_declarator_1 function_arglist COLONCOLON start + { $$.comp = $1.comp; + *$1.last = $2.comp; + $$.last = $2.last; + $$.comp = fill_comp (DEMANGLE_COMPONENT_LOCAL_NAME, $$.comp, $4); + } + ; + +direct_declarator_1 + : '(' ptr_operator declarator ')' + { $$.comp = $3.comp; + $$.last = $2.last; + *$3.last = $2.comp; } + | direct_declarator_1 function_arglist + { $$.comp = $1.comp; + *$1.last = $2.comp; + $$.last = $2.last; + } + | direct_declarator_1 array_indicator + { $$.comp = $1.comp; + *$1.last = $2; + $$.last = &d_right ($2); + } + | colon_ext_name function_arglist + { $$.comp = fill_comp (DEMANGLE_COMPONENT_TYPED_NAME, $1, $2.comp); + $$.last = $2.last; + } + | colon_ext_name array_indicator + { $$.comp = fill_comp (DEMANGLE_COMPONENT_TYPED_NAME, $1, $2); + $$.last = &d_right ($2); + } + ; + +exp : '(' exp1 ')' + { $$ = $2; } + ; + +/* Silly trick. Only allow '>' when parenthesized, in order to + handle conflict with templates. */ +exp1 : exp + ; + +exp1 : exp '>' exp + { $$ = d_binary (">", $1, $3); } + ; + +/* References. Not allowed everywhere in template parameters, only + at the top level, but treat them as expressions in case they are wrapped + in parentheses. */ +exp1 : '&' start + { $$ = fill_comp (DEMANGLE_COMPONENT_UNARY, make_operator ("&", 1), $2); } + ; + +/* Expressions, not including the comma operator. */ +exp : '-' exp %prec UNARY + { $$ = d_unary ("-", $2); } + ; + +exp : '!' exp %prec UNARY + { $$ = d_unary ("!", $2); } + ; + +exp : '~' exp %prec UNARY + { $$ = d_unary ("~", $2); } + ; + +/* Casts. First your normal C-style cast. If exp is a LITERAL, just change + its type. */ + +exp : '(' type ')' exp %prec UNARY + { if ($4->type == DEMANGLE_COMPONENT_LITERAL + || $4->type == DEMANGLE_COMPONENT_LITERAL_NEG) + { + $$ = $4; + d_left ($4) = $2; + } + else + $$ = fill_comp (DEMANGLE_COMPONENT_UNARY, + fill_comp (DEMANGLE_COMPONENT_CAST, $2, NULL), + $4); + } + ; + +/* Mangling does not differentiate between these, so we don't need to + either. */ +exp : STATIC_CAST '<' type '>' '(' exp1 ')' %prec UNARY + { $$ = fill_comp (DEMANGLE_COMPONENT_UNARY, + fill_comp (DEMANGLE_COMPONENT_CAST, $3, NULL), + $6); + } + ; + +exp : DYNAMIC_CAST '<' type '>' '(' exp1 ')' %prec UNARY + { $$ = fill_comp (DEMANGLE_COMPONENT_UNARY, + fill_comp (DEMANGLE_COMPONENT_CAST, $3, NULL), + $6); + } + ; + +exp : REINTERPRET_CAST '<' type '>' '(' exp1 ')' %prec UNARY + { $$ = fill_comp (DEMANGLE_COMPONENT_UNARY, + fill_comp (DEMANGLE_COMPONENT_CAST, $3, NULL), + $6); + } + ; + +/* Another form of C++-style cast. "type ( exp1 )" is not allowed (it's too + ambiguous), but "name ( exp1 )" is. Because we don't need to support + function types, we can handle this unambiguously (the use of typespec_2 + prevents a silly, harmless conflict with qualifiers_opt). This does not + appear in demangler output so it's not a great loss if we need to + disable it. */ +exp : typespec_2 '(' exp1 ')' %prec UNARY + { $$ = fill_comp (DEMANGLE_COMPONENT_UNARY, + fill_comp (DEMANGLE_COMPONENT_CAST, $1, NULL), + $3); + } + ; + +/* TO INVESTIGATE: ._0 style anonymous names; anonymous namespaces */ + +/* Binary operators in order of decreasing precedence. */ + +exp : exp '*' exp + { $$ = d_binary ("*", $1, $3); } + ; + +exp : exp '/' exp + { $$ = d_binary ("/", $1, $3); } + ; + +exp : exp '%' exp + { $$ = d_binary ("%", $1, $3); } + ; + +exp : exp '+' exp + { $$ = d_binary ("+", $1, $3); } + ; + +exp : exp '-' exp + { $$ = d_binary ("-", $1, $3); } + ; + +exp : exp LSH exp + { $$ = d_binary ("<<", $1, $3); } + ; + +exp : exp RSH exp + { $$ = d_binary (">>", $1, $3); } + ; + +exp : exp EQUAL exp + { $$ = d_binary ("==", $1, $3); } + ; + +exp : exp NOTEQUAL exp + { $$ = d_binary ("!=", $1, $3); } + ; + +exp : exp LEQ exp + { $$ = d_binary ("<=", $1, $3); } + ; + +exp : exp GEQ exp + { $$ = d_binary (">=", $1, $3); } + ; + +exp : exp '<' exp + { $$ = d_binary ("<", $1, $3); } + ; + +exp : exp '&' exp + { $$ = d_binary ("&", $1, $3); } + ; + +exp : exp '^' exp + { $$ = d_binary ("^", $1, $3); } + ; + +exp : exp '|' exp + { $$ = d_binary ("|", $1, $3); } + ; + +exp : exp ANDAND exp + { $$ = d_binary ("&&", $1, $3); } + ; + +exp : exp OROR exp + { $$ = d_binary ("||", $1, $3); } + ; + +/* Not 100% sure these are necessary, but they're harmless. */ +exp : exp ARROW NAME + { $$ = d_binary ("->", $1, $3); } + ; + +exp : exp '.' NAME + { $$ = d_binary (".", $1, $3); } + ; + +exp : exp '?' exp ':' exp %prec '?' + { $$ = fill_comp (DEMANGLE_COMPONENT_TRINARY, make_operator ("?", 3), + fill_comp (DEMANGLE_COMPONENT_TRINARY_ARG1, $1, + fill_comp (DEMANGLE_COMPONENT_TRINARY_ARG2, $3, $5))); + } + ; + +exp : INT + ; + +/* Not generally allowed. */ +exp : FLOAT + ; + +exp : SIZEOF '(' type ')' %prec UNARY + { $$ = d_unary ("sizeof", $3); } + ; + +/* C++. */ +exp : TRUEKEYWORD + { struct demangle_component *i; + i = make_name ("1", 1); + $$ = fill_comp (DEMANGLE_COMPONENT_LITERAL, + make_builtin_type ("bool"), + i); + } + ; + +exp : FALSEKEYWORD + { struct demangle_component *i; + i = make_name ("0", 1); + $$ = fill_comp (DEMANGLE_COMPONENT_LITERAL, + make_builtin_type ("bool"), + i); + } + ; + +/* end of C++. */ + +%% + +/* Apply QUALIFIERS to LHS and return a qualified component. IS_METHOD + is set if LHS is a method, in which case the qualifiers are logically + applied to "this". We apply qualifiers in a consistent order; LHS + may already be qualified; duplicate qualifiers are not created. */ + +struct demangle_component * +d_qualify (struct demangle_component *lhs, int qualifiers, int is_method) +{ + struct demangle_component **inner_p; + enum demangle_component_type type; + + /* For now the order is CONST (innermost), VOLATILE, RESTRICT. */ + +#define HANDLE_QUAL(TYPE, MTYPE, QUAL) \ + if ((qualifiers & QUAL) && (type != TYPE) && (type != MTYPE)) \ + { \ + *inner_p = fill_comp (is_method ? MTYPE : TYPE, \ + *inner_p, NULL); \ + inner_p = &d_left (*inner_p); \ + type = (*inner_p)->type; \ + } \ + else if (type == TYPE || type == MTYPE) \ + { \ + inner_p = &d_left (*inner_p); \ + type = (*inner_p)->type; \ + } + + inner_p = &lhs; + + type = (*inner_p)->type; + + HANDLE_QUAL (DEMANGLE_COMPONENT_RESTRICT, DEMANGLE_COMPONENT_RESTRICT_THIS, QUAL_RESTRICT); + HANDLE_QUAL (DEMANGLE_COMPONENT_VOLATILE, DEMANGLE_COMPONENT_VOLATILE_THIS, QUAL_VOLATILE); + HANDLE_QUAL (DEMANGLE_COMPONENT_CONST, DEMANGLE_COMPONENT_CONST_THIS, QUAL_CONST); + + return lhs; +} + +/* Return a builtin type corresponding to FLAGS. */ + +static struct demangle_component * +d_int_type (int flags) +{ + const char *name; + + switch (flags) + { + case INT_SIGNED | INT_CHAR: + name = "signed char"; + break; + case INT_CHAR: + name = "char"; + break; + case INT_UNSIGNED | INT_CHAR: + name = "unsigned char"; + break; + case 0: + case INT_SIGNED: + name = "int"; + break; + case INT_UNSIGNED: + name = "unsigned int"; + break; + case INT_LONG: + case INT_SIGNED | INT_LONG: + name = "long"; + break; + case INT_UNSIGNED | INT_LONG: + name = "unsigned long"; + break; + case INT_SHORT: + case INT_SIGNED | INT_SHORT: + name = "short"; + break; + case INT_UNSIGNED | INT_SHORT: + name = "unsigned short"; + break; + case INT_LLONG | INT_LONG: + case INT_SIGNED | INT_LLONG | INT_LONG: + name = "long long"; + break; + case INT_UNSIGNED | INT_LLONG | INT_LONG: + name = "unsigned long long"; + break; + default: + return NULL; + } + + return make_builtin_type (name); +} + +/* Wrapper to create a unary operation. */ + +static struct demangle_component * +d_unary (const char *name, struct demangle_component *lhs) +{ + return fill_comp (DEMANGLE_COMPONENT_UNARY, make_operator (name, 1), lhs); +} + +/* Wrapper to create a binary operation. */ + +static struct demangle_component * +d_binary (const char *name, struct demangle_component *lhs, struct demangle_component *rhs) +{ + return fill_comp (DEMANGLE_COMPONENT_BINARY, make_operator (name, 2), + fill_comp (DEMANGLE_COMPONENT_BINARY_ARGS, lhs, rhs)); +} + +/* Find the end of a symbol name starting at LEXPTR. */ + +static const char * +symbol_end (const char *lexptr) +{ + const char *p = lexptr; + + while (*p && (ISALNUM (*p) || *p == '_' || *p == '$' || *p == '.')) + p++; + + return p; +} + +/* Take care of parsing a number (anything that starts with a digit). + The number starts at P and contains LEN characters. Store the result in + YYLVAL. */ + +static int +parse_number (const char *p, int len, int parsed_float) +{ + int unsigned_p = 0; + + /* Number of "L" suffixes encountered. */ + int long_p = 0; + + struct demangle_component *signed_type; + struct demangle_component *unsigned_type; + struct demangle_component *type, *name; + enum demangle_component_type literal_type; + + if (p[0] == '-') + { + literal_type = DEMANGLE_COMPONENT_LITERAL_NEG; + p++; + len--; + } + else + literal_type = DEMANGLE_COMPONENT_LITERAL; + + if (parsed_float) + { + /* It's a float since it contains a point or an exponent. */ + char c; + + /* The GDB lexer checks the result of scanf at this point. Not doing + this leaves our error checking slightly weaker but only for invalid + data. */ + + /* See if it has `f' or `l' suffix (float or long double). */ + + c = TOLOWER (p[len - 1]); + + if (c == 'f') + { + len--; + type = make_builtin_type ("float"); + } + else if (c == 'l') + { + len--; + type = make_builtin_type ("long double"); + } + else if (ISDIGIT (c) || c == '.') + type = make_builtin_type ("double"); + else + return ERROR; + + name = make_name (p, len); + yylval.comp = fill_comp (literal_type, type, name); + + return FLOAT; + } + + /* This treats 0x1 and 1 as different literals. We also do not + automatically generate unsigned types. */ + + long_p = 0; + unsigned_p = 0; + while (len > 0) + { + if (p[len - 1] == 'l' || p[len - 1] == 'L') + { + len--; + long_p++; + continue; + } + if (p[len - 1] == 'u' || p[len - 1] == 'U') + { + len--; + unsigned_p++; + continue; + } + break; + } + + if (long_p == 0) + { + unsigned_type = make_builtin_type ("unsigned int"); + signed_type = make_builtin_type ("int"); + } + else if (long_p == 1) + { + unsigned_type = make_builtin_type ("unsigned long"); + signed_type = make_builtin_type ("long"); + } + else + { + unsigned_type = make_builtin_type ("unsigned long long"); + signed_type = make_builtin_type ("long long"); + } + + if (unsigned_p) + type = unsigned_type; + else + type = signed_type; + + name = make_name (p, len); + yylval.comp = fill_comp (literal_type, type, name); + + return INT; +} + +static char backslashable[] = "abefnrtv"; +static char represented[] = "\a\b\e\f\n\r\t\v"; + +/* Translate the backslash the way we would in the host character set. */ +static int +c_parse_backslash (int host_char, int *target_char) +{ + const char *ix; + ix = strchr (backslashable, host_char); + if (! ix) + return 0; + else + *target_char = represented[ix - backslashable]; + return 1; +} + +/* Parse a C escape sequence. STRING_PTR points to a variable + containing a pointer to the string to parse. That pointer + should point to the character after the \. That pointer + is updated past the characters we use. The value of the + escape sequence is returned. + + A negative value means the sequence \ newline was seen, + which is supposed to be equivalent to nothing at all. + + If \ is followed by a null character, we return a negative + value and leave the string pointer pointing at the null character. + + If \ is followed by 000, we return 0 and leave the string pointer + after the zeros. A value of 0 does not mean end of string. */ + +static int +parse_escape (const char **string_ptr) +{ + int target_char; + int c = *(*string_ptr)++; + if (c_parse_backslash (c, &target_char)) + return target_char; + else + switch (c) + { + case '\n': + return -2; + case 0: + (*string_ptr)--; + return 0; + case '^': + { + c = *(*string_ptr)++; + + if (c == '?') + return 0177; + else if (c == '\\') + target_char = parse_escape (string_ptr); + else + target_char = c; + + /* Now target_char is something like `c', and we want to find + its control-character equivalent. */ + target_char = target_char & 037; + + return target_char; + } + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + { + int i = c - '0'; + int count = 0; + while (++count < 3) + { + c = (**string_ptr); + if (c >= '0' && c <= '7') + { + (*string_ptr)++; + i *= 8; + i += c - '0'; + } + else + { + break; + } + } + return i; + } + default: + return target_char; + } +} + +#define HANDLE_SPECIAL(string, comp) \ + if (strncmp (tokstart, string, sizeof (string) - 1) == 0) \ + { \ + lexptr = tokstart + sizeof (string) - 1; \ + yylval.lval = comp; \ + return DEMANGLER_SPECIAL; \ + } + +#define HANDLE_TOKEN2(string, token) \ + if (lexptr[1] == string[1]) \ + { \ + lexptr += 2; \ + yylval.opname = string; \ + return token; \ + } + +#define HANDLE_TOKEN3(string, token) \ + if (lexptr[1] == string[1] && lexptr[2] == string[2]) \ + { \ + lexptr += 3; \ + yylval.opname = string; \ + return token; \ + } + +/* Read one token, getting characters through LEXPTR. */ + +static int +yylex (void) +{ + int c; + int namelen; + const char *tokstart, *tokptr; + + retry: + prev_lexptr = lexptr; + tokstart = lexptr; + + switch (c = *tokstart) + { + case 0: + return 0; + + case ' ': + case '\t': + case '\n': + lexptr++; + goto retry; + + case '\'': + /* We either have a character constant ('0' or '\177' for example) + or we have a quoted symbol reference ('foo(int,int)' in C++ + for example). */ + lexptr++; + c = *lexptr++; + if (c == '\\') + c = parse_escape (&lexptr); + else if (c == '\'') + { + yyerror ("empty character constant"); + return ERROR; + } + + c = *lexptr++; + if (c != '\'') + { + yyerror ("invalid character constant"); + return ERROR; + } + + /* FIXME: We should refer to a canonical form of the character, + presumably the same one that appears in manglings - the decimal + representation. But if that isn't in our input then we have to + allocate memory for it somewhere. */ + yylval.comp = fill_comp (DEMANGLE_COMPONENT_LITERAL, + make_builtin_type ("char"), + make_name (tokstart, lexptr - tokstart)); + + return INT; + + case '(': + if (strncmp (tokstart, "(anonymous namespace)", 21) == 0) + { + lexptr += 21; + yylval.comp = make_name ("(anonymous namespace)", + sizeof "(anonymous namespace)" - 1); + return NAME; + } + /* FALL THROUGH */ + + case ')': + case ',': + lexptr++; + return c; + + case '.': + if (lexptr[1] == '.' && lexptr[2] == '.') + { + lexptr += 3; + return ELLIPSIS; + } + + /* Might be a floating point number. */ + if (lexptr[1] < '0' || lexptr[1] > '9') + goto symbol; /* Nope, must be a symbol. */ + + goto try_number; + + case '-': + HANDLE_TOKEN2 ("-=", ASSIGN_MODIFY); + HANDLE_TOKEN2 ("--", DECREMENT); + HANDLE_TOKEN2 ("->", ARROW); + + /* For construction vtables. This is kind of hokey. */ + if (strncmp (tokstart, "-in-", 4) == 0) + { + lexptr += 4; + return CONSTRUCTION_IN; + } + + if (lexptr[1] < '0' || lexptr[1] > '9') + { + lexptr++; + return '-'; + } + /* FALL THRU into number case. */ + + try_number: + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + /* It's a number. */ + int got_dot = 0, got_e = 0, toktype; + const char *p = tokstart; + int hex = 0; + + if (c == '-') + p++; + + if (c == '0' && (p[1] == 'x' || p[1] == 'X')) + { + p += 2; + hex = 1; + } + else if (c == '0' && (p[1]=='t' || p[1]=='T' || p[1]=='d' || p[1]=='D')) + { + p += 2; + hex = 0; + } + + for (;; ++p) + { + /* This test includes !hex because 'e' is a valid hex digit + and thus does not indicate a floating point number when + the radix is hex. */ + if (!hex && !got_e && (*p == 'e' || *p == 'E')) + got_dot = got_e = 1; + /* This test does not include !hex, because a '.' always indicates + a decimal floating point number regardless of the radix. + + NOTE drow/2005-03-09: This comment is not accurate in C99; + however, it's not clear that all the floating point support + in this file is doing any good here. */ + else if (!got_dot && *p == '.') + got_dot = 1; + else if (got_e && (p[-1] == 'e' || p[-1] == 'E') + && (*p == '-' || *p == '+')) + /* This is the sign of the exponent, not the end of the + number. */ + continue; + /* We will take any letters or digits. parse_number will + complain if past the radix, or if L or U are not final. */ + else if (! ISALNUM (*p)) + break; + } + toktype = parse_number (tokstart, p - tokstart, got_dot|got_e); + if (toktype == ERROR) + { + char *err_copy = (char *) alloca (p - tokstart + 1); + + memcpy (err_copy, tokstart, p - tokstart); + err_copy[p - tokstart] = 0; + yyerror ("invalid number"); + return ERROR; + } + lexptr = p; + return toktype; + } + + case '+': + HANDLE_TOKEN2 ("+=", ASSIGN_MODIFY); + HANDLE_TOKEN2 ("++", INCREMENT); + lexptr++; + return c; + case '*': + HANDLE_TOKEN2 ("*=", ASSIGN_MODIFY); + lexptr++; + return c; + case '/': + HANDLE_TOKEN2 ("/=", ASSIGN_MODIFY); + lexptr++; + return c; + case '%': + HANDLE_TOKEN2 ("%=", ASSIGN_MODIFY); + lexptr++; + return c; + case '|': + HANDLE_TOKEN2 ("|=", ASSIGN_MODIFY); + HANDLE_TOKEN2 ("||", OROR); + lexptr++; + return c; + case '&': + HANDLE_TOKEN2 ("&=", ASSIGN_MODIFY); + HANDLE_TOKEN2 ("&&", ANDAND); + lexptr++; + return c; + case '^': + HANDLE_TOKEN2 ("^=", ASSIGN_MODIFY); + lexptr++; + return c; + case '!': + HANDLE_TOKEN2 ("!=", NOTEQUAL); + lexptr++; + return c; + case '<': + HANDLE_TOKEN3 ("<<=", ASSIGN_MODIFY); + HANDLE_TOKEN2 ("<=", LEQ); + HANDLE_TOKEN2 ("<<", LSH); + lexptr++; + return c; + case '>': + HANDLE_TOKEN3 (">>=", ASSIGN_MODIFY); + HANDLE_TOKEN2 (">=", GEQ); + HANDLE_TOKEN2 (">>", RSH); + lexptr++; + return c; + case '=': + HANDLE_TOKEN2 ("==", EQUAL); + lexptr++; + return c; + case ':': + HANDLE_TOKEN2 ("::", COLONCOLON); + lexptr++; + return c; + + case '[': + case ']': + case '?': + case '@': + case '~': + case '{': + case '}': + symbol: + lexptr++; + return c; + + case '"': + /* These can't occur in C++ names. */ + yyerror ("unexpected string literal"); + return ERROR; + } + + if (!(c == '_' || c == '$' || ISALPHA (c))) + { + /* We must have come across a bad character (e.g. ';'). */ + yyerror ("invalid character"); + return ERROR; + } + + /* It's a name. See how long it is. */ + namelen = 0; + do + c = tokstart[++namelen]; + while (ISALNUM (c) || c == '_' || c == '$'); + + lexptr += namelen; + + /* Catch specific keywords. Notice that some of the keywords contain + spaces, and are sorted by the length of the first word. They must + all include a trailing space in the string comparison. */ + switch (namelen) + { + case 16: + if (strncmp (tokstart, "reinterpret_cast", 16) == 0) + return REINTERPRET_CAST; + break; + case 12: + if (strncmp (tokstart, "construction vtable for ", 24) == 0) + { + lexptr = tokstart + 24; + return CONSTRUCTION_VTABLE; + } + if (strncmp (tokstart, "dynamic_cast", 12) == 0) + return DYNAMIC_CAST; + break; + case 11: + if (strncmp (tokstart, "static_cast", 11) == 0) + return STATIC_CAST; + break; + case 9: + HANDLE_SPECIAL ("covariant return thunk to ", DEMANGLE_COMPONENT_COVARIANT_THUNK); + HANDLE_SPECIAL ("reference temporary for ", DEMANGLE_COMPONENT_REFTEMP); + break; + case 8: + HANDLE_SPECIAL ("typeinfo for ", DEMANGLE_COMPONENT_TYPEINFO); + HANDLE_SPECIAL ("typeinfo fn for ", DEMANGLE_COMPONENT_TYPEINFO_FN); + HANDLE_SPECIAL ("typeinfo name for ", DEMANGLE_COMPONENT_TYPEINFO_NAME); + if (strncmp (tokstart, "operator", 8) == 0) + return OPERATOR; + if (strncmp (tokstart, "restrict", 8) == 0) + return RESTRICT; + if (strncmp (tokstart, "unsigned", 8) == 0) + return UNSIGNED; + if (strncmp (tokstart, "template", 8) == 0) + return TEMPLATE; + if (strncmp (tokstart, "volatile", 8) == 0) + return VOLATILE_KEYWORD; + break; + case 7: + HANDLE_SPECIAL ("virtual thunk to ", DEMANGLE_COMPONENT_VIRTUAL_THUNK); + if (strncmp (tokstart, "wchar_t", 7) == 0) + return WCHAR_T; + break; + case 6: + if (strncmp (tokstart, "global constructors keyed to ", 29) == 0) + { + const char *p; + lexptr = tokstart + 29; + yylval.typed_val_int.val = GLOBAL_CONSTRUCTORS; + /* Find the end of the symbol. */ + p = symbol_end (lexptr); + yylval.typed_val_int.type = make_name (lexptr, p - lexptr); + lexptr = p; + return GLOBAL; + } + if (strncmp (tokstart, "global destructors keyed to ", 28) == 0) + { + const char *p; + lexptr = tokstart + 28; + yylval.typed_val_int.val = GLOBAL_DESTRUCTORS; + /* Find the end of the symbol. */ + p = symbol_end (lexptr); + yylval.typed_val_int.type = make_name (lexptr, p - lexptr); + lexptr = p; + return GLOBAL; + } + + HANDLE_SPECIAL ("vtable for ", DEMANGLE_COMPONENT_VTABLE); + if (strncmp (tokstart, "delete", 6) == 0) + return DELETE; + if (strncmp (tokstart, "struct", 6) == 0) + return STRUCT; + if (strncmp (tokstart, "signed", 6) == 0) + return SIGNED_KEYWORD; + if (strncmp (tokstart, "sizeof", 6) == 0) + return SIZEOF; + if (strncmp (tokstart, "double", 6) == 0) + return DOUBLE_KEYWORD; + break; + case 5: + HANDLE_SPECIAL ("guard variable for ", DEMANGLE_COMPONENT_GUARD); + if (strncmp (tokstart, "false", 5) == 0) + return FALSEKEYWORD; + if (strncmp (tokstart, "class", 5) == 0) + return CLASS; + if (strncmp (tokstart, "union", 5) == 0) + return UNION; + if (strncmp (tokstart, "float", 5) == 0) + return FLOAT_KEYWORD; + if (strncmp (tokstart, "short", 5) == 0) + return SHORT; + if (strncmp (tokstart, "const", 5) == 0) + return CONST_KEYWORD; + break; + case 4: + if (strncmp (tokstart, "void", 4) == 0) + return VOID; + if (strncmp (tokstart, "bool", 4) == 0) + return BOOL; + if (strncmp (tokstart, "char", 4) == 0) + return CHAR; + if (strncmp (tokstart, "enum", 4) == 0) + return ENUM; + if (strncmp (tokstart, "long", 4) == 0) + return LONG; + if (strncmp (tokstart, "true", 4) == 0) + return TRUEKEYWORD; + break; + case 3: + HANDLE_SPECIAL ("VTT for ", DEMANGLE_COMPONENT_VTT); + HANDLE_SPECIAL ("non-virtual thunk to ", DEMANGLE_COMPONENT_THUNK); + if (strncmp (tokstart, "new", 3) == 0) + return NEW; + if (strncmp (tokstart, "int", 3) == 0) + return INT_KEYWORD; + break; + default: + break; + } + + yylval.comp = make_name (tokstart, namelen); + return NAME; +} + +static void +yyerror (char *msg) +{ + if (global_errmsg) + return; + + error_lexptr = prev_lexptr; + global_errmsg = msg ? msg : "parse error"; +} + +/* Allocate all the components we'll need to build a tree. We generally + allocate too many components, but the extra memory usage doesn't hurt + because the trees are temporary. If we start keeping the trees for + a longer lifetime we'll need to be cleverer. */ +static struct demangle_info * +allocate_info (int comps) +{ + struct demangle_info *ret; + + ret = malloc (sizeof (struct demangle_info) + + sizeof (struct demangle_component) * (comps - 1)); + ret->used = 0; + return ret; +} + +/* Convert RESULT to a string. The return value is allocated + using xmalloc. ESTIMATED_LEN is used only as a guide to the + length of the result. This functions handles a few cases that + cplus_demangle_print does not, specifically the global destructor + and constructor labels. */ + +char * +cp_comp_to_string (struct demangle_component *result, int estimated_len) +{ + char *str, *prefix = NULL, *buf; + size_t err = 0; + + if (result->type == GLOBAL_DESTRUCTORS) + { + result = d_left (result); + prefix = "global destructors keyed to "; + } + else if (result->type == GLOBAL_CONSTRUCTORS) + { + result = d_left (result); + prefix = "global constructors keyed to "; + } + + str = cplus_demangle_print (DMGL_PARAMS | DMGL_ANSI, result, estimated_len, &err); + if (str == NULL) + return NULL; + + if (prefix == NULL) + return str; + + buf = malloc (strlen (str) + strlen (prefix) + 1); + strcpy (buf, prefix); + strcat (buf, str); + free (str); + return (buf); +} + +/* Convert a demangled name to a demangle_component tree. *MEMORY is set to the + block of used memory that should be freed when finished with the + tree. On error, NULL is returned, and an error message will be + set in *ERRMSG (which does not need to be freed). */ + +struct demangle_component * +cp_demangled_name_to_comp (const char *demangled_name, void **memory, + const char **errmsg) +{ + static char errbuf[60]; + struct demangle_component *result; + + int len = strlen (demangled_name); + + len = len + len / 8; + prev_lexptr = lexptr = demangled_name; + error_lexptr = NULL; + global_errmsg = NULL; + + demangle_info = allocate_info (len); + + if (yyparse ()) + { + if (global_errmsg && errmsg) + { + snprintf (errbuf, sizeof (errbuf) - 2, "%s, near `%s", + global_errmsg, error_lexptr); + strcat (errbuf, "'"); + *errmsg = errbuf; + } + free (demangle_info); + return NULL; + } + + *memory = demangle_info; + result = global_result; + global_result = NULL; + + return result; +} + +#ifdef TEST_CPNAMES + +static void +cp_print (struct demangle_component *result) +{ + char *str; + size_t err = 0; + + if (result->type == GLOBAL_DESTRUCTORS) + { + result = d_left (result); + fputs ("global destructors keyed to ", stdout); + } + else if (result->type == GLOBAL_CONSTRUCTORS) + { + result = d_left (result); + fputs ("global constructors keyed to ", stdout); + } + + str = cplus_demangle_print (DMGL_PARAMS | DMGL_ANSI, result, 64, &err); + if (str == NULL) + return; + + fputs (str, stdout); + + free (str); +} + +static char +trim_chars (char *lexptr, char **extra_chars) +{ + char *p = (char *) symbol_end (lexptr); + char c = 0; + + if (*p) + { + c = *p; + *p = 0; + *extra_chars = p + 1; + } + + return c; +} + +int +main (int argc, char **argv) +{ + char *str2, *extra_chars, c; + char buf[65536]; + int arg; + const char *errmsg; + void *memory; + struct demangle_component *result; + + arg = 1; + if (argv[arg] && strcmp (argv[arg], "--debug") == 0) + { + yydebug = 1; + arg++; + } + + if (argv[arg] == NULL) + while (fgets (buf, 65536, stdin) != NULL) + { + int len; + buf[strlen (buf) - 1] = 0; + /* Use DMGL_VERBOSE to get expanded standard substitutions. */ + c = trim_chars (buf, &extra_chars); + str2 = cplus_demangle (buf, DMGL_PARAMS | DMGL_ANSI | DMGL_VERBOSE); + if (str2 == NULL) + { + /* printf ("Demangling error\n"); */ + if (c) + printf ("%s%c%s\n", buf, c, extra_chars); + else + printf ("%s\n", buf); + continue; + } + result = cp_demangled_name_to_comp (str2, &memory, &errmsg); + if (result == NULL) + { + fputs (errmsg, stderr); + fputc ('\n', stderr); + continue; + } + + cp_print (result); + free (memory); + + free (str2); + if (c) + { + putchar (c); + fputs (extra_chars, stdout); + } + putchar ('\n'); + } + else + { + result = cp_demangled_name_to_comp (argv[arg], &memory, &errmsg); + if (result == NULL) + { + fputs (errmsg, stderr); + fputc ('\n', stderr); + return 0; + } + cp_print (result); + putchar ('\n'); + free (memory); + } + return 0; +} + +#endif diff --git a/gdb/cp-support.c b/gdb/cp-support.c index ecda4a5395..9f39b5acc0 100644 --- a/gdb/cp-support.c +++ b/gdb/cp-support.c @@ -1,5 +1,5 @@ /* Helper routines for C++ support in GDB. - Copyright 2002, 2003 Free Software Foundation, Inc. + Copyright 2002, 2003, 2004, 2005 Free Software Foundation, Inc. Contributed by MontaVista Software. @@ -35,9 +35,10 @@ #include "complaints.h" #include "gdbtypes.h" -/* Functions related to demangled name parsing. */ +#define d_left(dc) (dc)->u.s_binary.left +#define d_right(dc) (dc)->u.s_binary.right -static const char *find_last_component (const char *name); +/* Functions related to demangled name parsing. */ static unsigned int cp_find_first_component_aux (const char *name, int permissive); @@ -71,78 +72,76 @@ struct cmd_list_element *maint_cplus_cmd_list = NULL; static void maint_cplus_command (char *arg, int from_tty); static void first_component_command (char *arg, int from_tty); -/* Here are some random pieces of trivia to keep in mind while trying - to take apart demangled names: +/* Return the canonicalized form of STRING, or NULL if STRING can not be + parsed. The return value is allocated via xmalloc. - - Names can contain function arguments or templates, so the process - has to be, to some extent recursive: maybe keep track of your - depth based on encountering <> and (). + drow/2005-03-07: Should we also return NULL for things that trivially do + not require any change? e.g. simple identifiers. This could be more + efficient. */ - - Parentheses don't just have to happen at the end of a name: they - can occur even if the name in question isn't a function, because - a template argument might be a type that's a function. +char * +cp_canonicalize_string (const char *string) +{ + void *storage; + struct demangle_component *ret_comp; + char *ret; + int len = strlen (string); - - Conversely, even if you're trying to deal with a function, its - demangled name might not end with ')': it could be a const or - volatile class method, in which case it ends with "const" or - "volatile". + len = len + len / 8; - - Parentheses are also used in anonymous namespaces: a variable - 'foo' in an anonymous namespace gets demangled as "(anonymous - namespace)::foo". + ret_comp = cp_demangled_name_to_comp (string, &storage, NULL); + if (ret_comp == NULL) + return NULL; - - And operator names can contain parentheses or angle brackets. */ + ret = cp_comp_to_string (ret_comp, len); -/* FIXME: carlton/2003-03-13: We have several functions here with - overlapping functionality; can we combine them? Also, do they - handle all the above considerations correctly? */ + xfree (storage); -/* Find the last component of the demangled C++ name NAME. NAME - must be a method name including arguments, in order to correctly - locate the last component. + return ret; +} - This function return a pointer to the first colon before the - last component, or NULL if the name had only one component. */ +/* Convert a mangled name to a demangle_component tree. *MEMORY is set to the + block of used memory that should be freed when finished with the tree. + DEMANGLED_P is set to the char * that should be freed when finished with + the tree, or NULL if none was needed. OPTIONS will be passed to the + demangler. */ -static const char * -find_last_component (const char *name) +static struct demangle_component * +mangled_name_to_comp (const char *mangled_name, int options, + void **memory, char **demangled_p) { - const char *p; - int depth; - - /* Functions can have local classes, so we need to find the - beginning of the last argument list, not the end of the first - one. */ - p = name + strlen (name) - 1; - while (p > name && *p != ')') - p--; - - if (p == name) - return NULL; + struct demangle_component *ret; + char *demangled_name; + int len; - /* P now points at the `)' at the end of the argument list. Walk - back to the beginning. */ - p--; - depth = 1; - while (p > name && depth > 0) + /* If it looks like a v3 mangled name, then try to go directly + to trees. */ + if (mangled_name[0] == '_' && mangled_name[1] == 'Z') { - if (*p == '<' || *p == '(') - depth--; - else if (*p == '>' || *p == ')') - depth++; - p--; + ret = cplus_demangle_v3_components (mangled_name, options, memory); + if (ret) + { + *demangled_p = NULL; + return ret; + } } - if (p == name) - return NULL; - - while (p > name && *p != ':') - p--; + /* If it doesn't, or if that failed, then try to demangle the name. */ + demangled_name = cplus_demangle (mangled_name, options); + if (demangled_name == NULL) + return NULL; + + /* If we could demangle the name, parse it to build the component tree. */ + ret = cp_demangled_name_to_comp (demangled_name, memory, NULL); - if (p == name || p == name + 1 || p[-1] != ':') - return NULL; + if (ret == NULL) + { + free (demangled_name); + return NULL; + } - return p - 1; + *demangled_p = demangled_name; + return ret; } /* Return the name of the class containing method PHYSNAME. */ @@ -150,23 +149,66 @@ find_last_component (const char *name) char * cp_class_name_from_physname (const char *physname) { - char *ret = NULL; - const char *end; - int depth = 0; - char *demangled_name = cplus_demangle (physname, DMGL_ANSI | DMGL_PARAMS); - - if (demangled_name == NULL) + void *storage; + char *demangled_name = NULL, *ret; + struct demangle_component *ret_comp, *prev_comp; + int done; + + ret_comp = mangled_name_to_comp (physname, DMGL_ANSI, &storage, + &demangled_name); + if (ret_comp == NULL) return NULL; - end = find_last_component (demangled_name); - if (end != NULL) + done = 0; + prev_comp = NULL; + while (!done) + switch (ret_comp->type) + { + case DEMANGLE_COMPONENT_TYPED_NAME: + prev_comp = NULL; + ret_comp = d_right (ret_comp); + break; + case DEMANGLE_COMPONENT_QUAL_NAME: + case DEMANGLE_COMPONENT_LOCAL_NAME: + prev_comp = ret_comp; + ret_comp = d_right (ret_comp); + break; + case DEMANGLE_COMPONENT_CONST: + case DEMANGLE_COMPONENT_RESTRICT: + case DEMANGLE_COMPONENT_VOLATILE: + case DEMANGLE_COMPONENT_CONST_THIS: + case DEMANGLE_COMPONENT_RESTRICT_THIS: + case DEMANGLE_COMPONENT_VOLATILE_THIS: + case DEMANGLE_COMPONENT_VENDOR_TYPE_QUAL: + prev_comp = NULL; + ret_comp = d_left (ret_comp); + break; + case DEMANGLE_COMPONENT_NAME: + case DEMANGLE_COMPONENT_TEMPLATE: + case DEMANGLE_COMPONENT_CTOR: + case DEMANGLE_COMPONENT_DTOR: + case DEMANGLE_COMPONENT_OPERATOR: + case DEMANGLE_COMPONENT_EXTENDED_OPERATOR: + done = 1; + break; + default: + done = 1; + prev_comp = NULL; + ret_comp = NULL; + break; + } + + ret = NULL; + if (prev_comp != NULL) { - ret = xmalloc (end - demangled_name + 1); - memcpy (ret, demangled_name, end - demangled_name); - ret[end - demangled_name] = '\0'; + *prev_comp = *d_left (prev_comp); + /* The ten is completely arbitrary; we don't have a good estimate. */ + ret = cp_comp_to_string (prev_comp, 10); } - xfree (demangled_name); + xfree (storage); + if (demangled_name) + xfree (demangled_name); return ret; } @@ -175,42 +217,85 @@ cp_class_name_from_physname (const char *physname) char * method_name_from_physname (const char *physname) { - char *ret = NULL; - const char *end; - int depth = 0; - char *demangled_name = cplus_demangle (physname, DMGL_ANSI | DMGL_PARAMS); - - if (demangled_name == NULL) + void *storage; + char *demangled_name = NULL, *ret; + struct demangle_component *ret_comp; + int done; + + ret_comp = mangled_name_to_comp (physname, DMGL_ANSI, &storage, + &demangled_name); + if (ret_comp == NULL) return NULL; - end = find_last_component (demangled_name); - if (end != NULL) - { - char *args; - int len; + done = 0; + while (!done) + switch (ret_comp->type) + { + case DEMANGLE_COMPONENT_QUAL_NAME: + case DEMANGLE_COMPONENT_LOCAL_NAME: + case DEMANGLE_COMPONENT_TYPED_NAME: + ret_comp = d_right (ret_comp); + break; + case DEMANGLE_COMPONENT_CONST: + case DEMANGLE_COMPONENT_RESTRICT: + case DEMANGLE_COMPONENT_VOLATILE: + case DEMANGLE_COMPONENT_CONST_THIS: + case DEMANGLE_COMPONENT_RESTRICT_THIS: + case DEMANGLE_COMPONENT_VOLATILE_THIS: + case DEMANGLE_COMPONENT_VENDOR_TYPE_QUAL: + ret_comp = d_left (ret_comp); + break; + case DEMANGLE_COMPONENT_NAME: + case DEMANGLE_COMPONENT_TEMPLATE: + case DEMANGLE_COMPONENT_CTOR: + case DEMANGLE_COMPONENT_DTOR: + case DEMANGLE_COMPONENT_OPERATOR: + case DEMANGLE_COMPONENT_EXTENDED_OPERATOR: + done = 1; + break; + default: + done = 1; + ret_comp = NULL; + break; + } + + ret = NULL; + if (ret_comp != NULL) + /* The ten is completely arbitrary; we don't have a good estimate. */ + ret = cp_comp_to_string (ret_comp, 10); + + xfree (storage); + if (demangled_name) + xfree (demangled_name); + return ret; +} - /* Skip "::". */ - end = end + 2; +/* Here are some random pieces of trivia to keep in mind while trying + to take apart demangled names: - /* Find the argument list, if any. */ - args = strchr (end, '('); - if (args == NULL) - len = strlen (end + 2); - else - { - args --; - while (*args == ' ') - args --; - len = args - end + 1; - } - ret = xmalloc (len + 1); - memcpy (ret, end, len); - ret[len] = 0; - } + - Names can contain function arguments or templates, so the process + has to be, to some extent recursive: maybe keep track of your + depth based on encountering <> and (). + + - Parentheses don't just have to happen at the end of a name: they + can occur even if the name in question isn't a function, because + a template argument might be a type that's a function. + + - Conversely, even if you're trying to deal with a function, its + demangled name might not end with ')': it could be a const or + volatile class method, in which case it ends with "const" or + "volatile". + + - Parentheses are also used in anonymous namespaces: a variable + 'foo' in an anonymous namespace gets demangled as "(anonymous + namespace)::foo". + + - And operator names can contain parentheses or angle brackets. */ + +/* FIXME: carlton/2003-03-13: We have several functions here with + overlapping functionality; can we combine them? Also, do they + handle all the above considerations correctly? */ - xfree (demangled_name); - return ret; -} /* This returns the length of first component of NAME, which should be the demangled name of a C++ variable/function/method/etc. diff --git a/gdb/cp-support.h b/gdb/cp-support.h index f463d598c6..4f8ae2cf63 100644 --- a/gdb/cp-support.h +++ b/gdb/cp-support.h @@ -1,5 +1,5 @@ /* Helper routines for C++ support in GDB. - Copyright 2002, 2003, 2004 Free Software Foundation, Inc. + Copyright 2002, 2003, 2004, 2005 Free Software Foundation, Inc. Contributed by MontaVista Software. Namespace support contributed by David Carlton. @@ -35,6 +35,7 @@ struct obstack; struct block; struct objfile; struct type; +struct demangle_component; /* This struct is designed to store data from using directives. It says that names from namespace INNER should be visible within @@ -52,6 +53,8 @@ struct using_direct /* Functions from cp-support.c. */ +extern char *cp_canonicalize_string (const char *string); + extern char *cp_class_name_from_physname (const char *physname); extern char *method_name_from_physname (const char *physname); @@ -113,6 +116,14 @@ extern void cp_check_possible_namespace_symbols (const char *name, struct type *cp_lookup_transparent_type (const char *name); +/* Functions from cp-names.y. */ + +extern struct demangle_component *cp_demangled_name_to_comp + (const char *demangled_name, void **memory_p, const char **errmsg); + +extern char *cp_comp_to_string (struct demangle_component *result, + int estimated_len); + /* The list of "maint cplus" commands. */ extern struct cmd_list_element *maint_cplus_cmd_list; -- 2.34.1