| /* Helper routines for C++ support in GDB. |
| Copyright (C) 2002-2022 Free Software Foundation, Inc. |
| |
| Contributed by MontaVista Software. |
| |
| 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 3 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, see <http://www.gnu.org/licenses/>. */ |
| |
| #include "defs.h" |
| #include "cp-support.h" |
| #include "demangle.h" |
| #include "gdbcmd.h" |
| #include "dictionary.h" |
| #include "objfiles.h" |
| #include "frame.h" |
| #include "symtab.h" |
| #include "block.h" |
| #include "complaints.h" |
| #include "gdbtypes.h" |
| #include "expression.h" |
| #include "value.h" |
| #include "cp-abi.h" |
| #include "namespace.h" |
| #include <signal.h> |
| #include "gdbsupport/gdb_setjmp.h" |
| #include "safe-ctype.h" |
| #include "gdbsupport/selftest.h" |
| #include "gdbsupport/gdb-sigmask.h" |
| #include <atomic> |
| #include "event-top.h" |
| #include "run-on-main-thread.h" |
| |
| #define d_left(dc) (dc)->u.s_binary.left |
| #define d_right(dc) (dc)->u.s_binary.right |
| |
| /* Functions related to demangled name parsing. */ |
| |
| static unsigned int cp_find_first_component_aux (const char *name, |
| int permissive); |
| |
| static void demangled_name_complaint (const char *name); |
| |
| /* Functions related to overload resolution. */ |
| |
| static void overload_list_add_symbol (struct symbol *sym, |
| const char *oload_name, |
| std::vector<symbol *> *overload_list); |
| |
| static void add_symbol_overload_list_using |
| (const char *func_name, const char *the_namespace, |
| std::vector<symbol *> *overload_list); |
| |
| static void add_symbol_overload_list_qualified |
| (const char *func_name, |
| std::vector<symbol *> *overload_list); |
| |
| /* The list of "maint cplus" commands. */ |
| |
| struct cmd_list_element *maint_cplus_cmd_list = NULL; |
| |
| /* A list of typedefs which should not be substituted by replace_typedefs. */ |
| static const char * const ignore_typedefs[] = |
| { |
| "std::istream", "std::iostream", "std::ostream", "std::string" |
| }; |
| |
| static void |
| replace_typedefs (struct demangle_parse_info *info, |
| struct demangle_component *ret_comp, |
| canonicalization_ftype *finder, |
| void *data); |
| |
| /* A convenience function to copy STRING into OBSTACK, returning a pointer |
| to the newly allocated string and saving the number of bytes saved in LEN. |
| |
| It does not copy the terminating '\0' byte! */ |
| |
| static char * |
| copy_string_to_obstack (struct obstack *obstack, const char *string, |
| long *len) |
| { |
| *len = strlen (string); |
| return (char *) obstack_copy (obstack, string, *len); |
| } |
| |
| /* Return 1 if STRING is clearly already in canonical form. This |
| function is conservative; things which it does not recognize are |
| assumed to be non-canonical, and the parser will sort them out |
| afterwards. This speeds up the critical path for alphanumeric |
| identifiers. */ |
| |
| static int |
| cp_already_canonical (const char *string) |
| { |
| /* Identifier start character [a-zA-Z_]. */ |
| if (!ISIDST (string[0])) |
| return 0; |
| |
| /* These are the only two identifiers which canonicalize to other |
| than themselves or an error: unsigned -> unsigned int and |
| signed -> int. */ |
| if (string[0] == 'u' && strcmp (&string[1], "nsigned") == 0) |
| return 0; |
| else if (string[0] == 's' && strcmp (&string[1], "igned") == 0) |
| return 0; |
| |
| /* Identifier character [a-zA-Z0-9_]. */ |
| while (ISIDNUM (string[1])) |
| string++; |
| |
| if (string[1] == '\0') |
| return 1; |
| else |
| return 0; |
| } |
| |
| /* Inspect the given RET_COMP for its type. If it is a typedef, |
| replace the node with the typedef's tree. |
| |
| Returns 1 if any typedef substitutions were made, 0 otherwise. */ |
| |
| static int |
| inspect_type (struct demangle_parse_info *info, |
| struct demangle_component *ret_comp, |
| canonicalization_ftype *finder, |
| void *data) |
| { |
| char *name; |
| struct symbol *sym; |
| |
| /* Copy the symbol's name from RET_COMP and look it up |
| in the symbol table. */ |
| name = (char *) alloca (ret_comp->u.s_name.len + 1); |
| memcpy (name, ret_comp->u.s_name.s, ret_comp->u.s_name.len); |
| name[ret_comp->u.s_name.len] = '\0'; |
| |
| /* Ignore any typedefs that should not be substituted. */ |
| for (const char *ignorable : ignore_typedefs) |
| { |
| if (strcmp (name, ignorable) == 0) |
| return 0; |
| } |
| |
| sym = NULL; |
| |
| try |
| { |
| sym = lookup_symbol (name, 0, VAR_DOMAIN, 0).symbol; |
| } |
| catch (const gdb_exception &except) |
| { |
| return 0; |
| } |
| |
| if (sym != NULL) |
| { |
| struct type *otype = sym->type (); |
| |
| if (finder != NULL) |
| { |
| const char *new_name = (*finder) (otype, data); |
| |
| if (new_name != NULL) |
| { |
| ret_comp->u.s_name.s = new_name; |
| ret_comp->u.s_name.len = strlen (new_name); |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| /* If the type is a typedef or namespace alias, replace it. */ |
| if (otype->code () == TYPE_CODE_TYPEDEF |
| || otype->code () == TYPE_CODE_NAMESPACE) |
| { |
| long len; |
| int is_anon; |
| struct type *type; |
| std::unique_ptr<demangle_parse_info> i; |
| |
| /* Get the real type of the typedef. */ |
| type = check_typedef (otype); |
| |
| /* If the symbol name is the same as the original type name, |
| don't substitute. That would cause infinite recursion in |
| symbol lookups, as the typedef symbol is often the first |
| found symbol in the symbol table. |
| |
| However, this can happen in a number of situations, such as: |
| |
| If the symbol is a namespace and its type name is no different |
| than the name we looked up, this symbol is not a namespace |
| alias and does not need to be substituted. |
| |
| If the symbol is typedef and its type name is the same |
| as the symbol's name, e.g., "typedef struct foo foo;". */ |
| if (type->name () != nullptr |
| && strcmp (type->name (), name) == 0) |
| return 0; |
| |
| is_anon = (type->name () == NULL |
| && (type->code () == TYPE_CODE_ENUM |
| || type->code () == TYPE_CODE_STRUCT |
| || type->code () == TYPE_CODE_UNION)); |
| if (is_anon) |
| { |
| struct type *last = otype; |
| |
| /* Find the last typedef for the type. */ |
| while (TYPE_TARGET_TYPE (last) != NULL |
| && (TYPE_TARGET_TYPE (last)->code () |
| == TYPE_CODE_TYPEDEF)) |
| last = TYPE_TARGET_TYPE (last); |
| |
| /* If there is only one typedef for this anonymous type, |
| do not substitute it. */ |
| if (type == otype) |
| return 0; |
| else |
| /* Use the last typedef seen as the type for this |
| anonymous type. */ |
| type = last; |
| } |
| |
| string_file buf; |
| try |
| { |
| type_print (type, "", &buf, -1); |
| } |
| /* If type_print threw an exception, there is little point |
| in continuing, so just bow out gracefully. */ |
| catch (const gdb_exception_error &except) |
| { |
| return 0; |
| } |
| |
| len = buf.size (); |
| name = obstack_strdup (&info->obstack, buf.string ()); |
| |
| /* Turn the result into a new tree. Note that this |
| tree will contain pointers into NAME, so NAME cannot |
| be free'd until all typedef conversion is done and |
| the final result is converted into a string. */ |
| i = cp_demangled_name_to_comp (name, NULL); |
| if (i != NULL) |
| { |
| /* Merge the two trees. */ |
| cp_merge_demangle_parse_infos (info, ret_comp, i.get ()); |
| |
| /* Replace any newly introduced typedefs -- but not |
| if the type is anonymous (that would lead to infinite |
| looping). */ |
| if (!is_anon) |
| replace_typedefs (info, ret_comp, finder, data); |
| } |
| else |
| { |
| /* This shouldn't happen unless the type printer has |
| output something that the name parser cannot grok. |
| Nonetheless, an ounce of prevention... |
| |
| Canonicalize the name again, and store it in the |
| current node (RET_COMP). */ |
| gdb::unique_xmalloc_ptr<char> canon |
| = cp_canonicalize_string_no_typedefs (name); |
| |
| if (canon != nullptr) |
| { |
| /* Copy the canonicalization into the obstack. */ |
| name = copy_string_to_obstack (&info->obstack, canon.get (), &len); |
| } |
| |
| ret_comp->u.s_name.s = name; |
| ret_comp->u.s_name.len = len; |
| } |
| |
| return 1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| /* Helper for replace_typedefs_qualified_name to handle |
| DEMANGLE_COMPONENT_TEMPLATE. TMPL is the template node. BUF is |
| the buffer that holds the qualified name being built by |
| replace_typedefs_qualified_name. REPL is the node that will be |
| rewritten as a DEMANGLE_COMPONENT_NAME node holding the 'template |
| plus template arguments' name with typedefs replaced. */ |
| |
| static bool |
| replace_typedefs_template (struct demangle_parse_info *info, |
| string_file &buf, |
| struct demangle_component *tmpl, |
| struct demangle_component *repl, |
| canonicalization_ftype *finder, |
| void *data) |
| { |
| demangle_component *tmpl_arglist = d_right (tmpl); |
| |
| /* Replace typedefs in the template argument list. */ |
| replace_typedefs (info, tmpl_arglist, finder, data); |
| |
| /* Convert 'template + replaced template argument list' to a string |
| and replace the REPL node. */ |
| gdb::unique_xmalloc_ptr<char> tmpl_str = cp_comp_to_string (tmpl, 100); |
| if (tmpl_str == nullptr) |
| { |
| /* If something went astray, abort typedef substitutions. */ |
| return false; |
| } |
| buf.puts (tmpl_str.get ()); |
| |
| repl->type = DEMANGLE_COMPONENT_NAME; |
| repl->u.s_name.s = obstack_strdup (&info->obstack, buf.string ()); |
| repl->u.s_name.len = buf.size (); |
| return true; |
| } |
| |
| /* Replace any typedefs appearing in the qualified name |
| (DEMANGLE_COMPONENT_QUAL_NAME) represented in RET_COMP for the name parse |
| given in INFO. */ |
| |
| static void |
| replace_typedefs_qualified_name (struct demangle_parse_info *info, |
| struct demangle_component *ret_comp, |
| canonicalization_ftype *finder, |
| void *data) |
| { |
| string_file buf; |
| struct demangle_component *comp = ret_comp; |
| |
| /* Walk each node of the qualified name, reconstructing the name of |
| this element. With every node, check for any typedef substitutions. |
| If a substitution has occurred, replace the qualified name node |
| with a DEMANGLE_COMPONENT_NAME node representing the new, typedef- |
| substituted name. */ |
| while (comp->type == DEMANGLE_COMPONENT_QUAL_NAME) |
| { |
| if (d_left (comp)->type == DEMANGLE_COMPONENT_TEMPLATE) |
| { |
| /* Convert 'template + replaced template argument list' to a |
| string and replace the top DEMANGLE_COMPONENT_QUAL_NAME |
| node. */ |
| if (!replace_typedefs_template (info, buf, |
| d_left (comp), d_left (ret_comp), |
| finder, data)) |
| return; |
| |
| buf.clear (); |
| d_right (ret_comp) = d_right (comp); |
| comp = ret_comp; |
| |
| /* Fallback to DEMANGLE_COMPONENT_NAME processing. We want |
| to call inspect_type for this template, in case we have a |
| template alias, like: |
| template<typename T> using alias = base<int, t>; |
| in which case we want inspect_type to do a replacement like: |
| alias<int> -> base<int, int> |
| */ |
| } |
| |
| if (d_left (comp)->type == DEMANGLE_COMPONENT_NAME) |
| { |
| struct demangle_component newobj; |
| |
| buf.write (d_left (comp)->u.s_name.s, d_left (comp)->u.s_name.len); |
| newobj.type = DEMANGLE_COMPONENT_NAME; |
| newobj.u.s_name.s = obstack_strdup (&info->obstack, buf.string ()); |
| newobj.u.s_name.len = buf.size (); |
| if (inspect_type (info, &newobj, finder, data)) |
| { |
| char *s; |
| long slen; |
| |
| /* A typedef was substituted in NEW. Convert it to a |
| string and replace the top DEMANGLE_COMPONENT_QUAL_NAME |
| node. */ |
| |
| buf.clear (); |
| gdb::unique_xmalloc_ptr<char> n |
| = cp_comp_to_string (&newobj, 100); |
| if (n == NULL) |
| { |
| /* If something went astray, abort typedef substitutions. */ |
| return; |
| } |
| |
| s = copy_string_to_obstack (&info->obstack, n.get (), &slen); |
| |
| d_left (ret_comp)->type = DEMANGLE_COMPONENT_NAME; |
| d_left (ret_comp)->u.s_name.s = s; |
| d_left (ret_comp)->u.s_name.len = slen; |
| d_right (ret_comp) = d_right (comp); |
| comp = ret_comp; |
| continue; |
| } |
| } |
| else |
| { |
| /* The current node is not a name, so simply replace any |
| typedefs in it. Then print it to the stream to continue |
| checking for more typedefs in the tree. */ |
| replace_typedefs (info, d_left (comp), finder, data); |
| gdb::unique_xmalloc_ptr<char> name |
| = cp_comp_to_string (d_left (comp), 100); |
| if (name == NULL) |
| { |
| /* If something went astray, abort typedef substitutions. */ |
| return; |
| } |
| buf.puts (name.get ()); |
| } |
| |
| buf.write ("::", 2); |
| comp = d_right (comp); |
| } |
| |
| /* If the next component is DEMANGLE_COMPONENT_TEMPLATE or |
| DEMANGLE_COMPONENT_NAME, save the qualified name assembled above |
| and append the name given by COMP. Then use this reassembled |
| name to check for a typedef. */ |
| |
| if (comp->type == DEMANGLE_COMPONENT_TEMPLATE) |
| { |
| /* Replace the top (DEMANGLE_COMPONENT_QUAL_NAME) node with a |
| DEMANGLE_COMPONENT_NAME node containing the whole name. */ |
| if (!replace_typedefs_template (info, buf, comp, ret_comp, finder, data)) |
| return; |
| inspect_type (info, ret_comp, finder, data); |
| } |
| else if (comp->type == DEMANGLE_COMPONENT_NAME) |
| { |
| buf.write (comp->u.s_name.s, comp->u.s_name.len); |
| |
| /* Replace the top (DEMANGLE_COMPONENT_QUAL_NAME) node |
| with a DEMANGLE_COMPONENT_NAME node containing the whole |
| name. */ |
| ret_comp->type = DEMANGLE_COMPONENT_NAME; |
| ret_comp->u.s_name.s = obstack_strdup (&info->obstack, buf.string ()); |
| ret_comp->u.s_name.len = buf.size (); |
| inspect_type (info, ret_comp, finder, data); |
| } |
| else |
| replace_typedefs (info, comp, finder, data); |
| } |
| |
| |
| /* A function to check const and volatile qualifiers for argument types. |
| |
| "Parameter declarations that differ only in the presence |
| or absence of `const' and/or `volatile' are equivalent." |
| C++ Standard N3290, clause 13.1.3 #4. */ |
| |
| static void |
| check_cv_qualifiers (struct demangle_component *ret_comp) |
| { |
| while (d_left (ret_comp) != NULL |
| && (d_left (ret_comp)->type == DEMANGLE_COMPONENT_CONST |
| || d_left (ret_comp)->type == DEMANGLE_COMPONENT_VOLATILE)) |
| { |
| d_left (ret_comp) = d_left (d_left (ret_comp)); |
| } |
| } |
| |
| /* Walk the parse tree given by RET_COMP, replacing any typedefs with |
| their basic types. */ |
| |
| static void |
| replace_typedefs (struct demangle_parse_info *info, |
| struct demangle_component *ret_comp, |
| canonicalization_ftype *finder, |
| void *data) |
| { |
| if (ret_comp) |
| { |
| if (finder != NULL |
| && (ret_comp->type == DEMANGLE_COMPONENT_NAME |
| || ret_comp->type == DEMANGLE_COMPONENT_QUAL_NAME |
| || ret_comp->type == DEMANGLE_COMPONENT_TEMPLATE |
| || ret_comp->type == DEMANGLE_COMPONENT_BUILTIN_TYPE)) |
| { |
| gdb::unique_xmalloc_ptr<char> local_name |
| = cp_comp_to_string (ret_comp, 10); |
| |
| if (local_name != NULL) |
| { |
| struct symbol *sym = NULL; |
| |
| sym = NULL; |
| try |
| { |
| sym = lookup_symbol (local_name.get (), 0, |
| VAR_DOMAIN, 0).symbol; |
| } |
| catch (const gdb_exception &except) |
| { |
| } |
| |
| if (sym != NULL) |
| { |
| struct type *otype = sym->type (); |
| const char *new_name = (*finder) (otype, data); |
| |
| if (new_name != NULL) |
| { |
| ret_comp->type = DEMANGLE_COMPONENT_NAME; |
| ret_comp->u.s_name.s = new_name; |
| ret_comp->u.s_name.len = strlen (new_name); |
| return; |
| } |
| } |
| } |
| } |
| |
| switch (ret_comp->type) |
| { |
| case DEMANGLE_COMPONENT_ARGLIST: |
| check_cv_qualifiers (ret_comp); |
| /* Fall through */ |
| |
| case DEMANGLE_COMPONENT_FUNCTION_TYPE: |
| case DEMANGLE_COMPONENT_TEMPLATE: |
| case DEMANGLE_COMPONENT_TEMPLATE_ARGLIST: |
| case DEMANGLE_COMPONENT_TYPED_NAME: |
| replace_typedefs (info, d_left (ret_comp), finder, data); |
| replace_typedefs (info, d_right (ret_comp), finder, data); |
| break; |
| |
| case DEMANGLE_COMPONENT_NAME: |
| inspect_type (info, ret_comp, finder, data); |
| break; |
| |
| case DEMANGLE_COMPONENT_QUAL_NAME: |
| replace_typedefs_qualified_name (info, ret_comp, finder, data); |
| break; |
| |
| case DEMANGLE_COMPONENT_LOCAL_NAME: |
| case DEMANGLE_COMPONENT_CTOR: |
| case DEMANGLE_COMPONENT_ARRAY_TYPE: |
| case DEMANGLE_COMPONENT_PTRMEM_TYPE: |
| replace_typedefs (info, d_right (ret_comp), finder, data); |
| break; |
| |
| case DEMANGLE_COMPONENT_CONST: |
| case DEMANGLE_COMPONENT_RESTRICT: |
| case DEMANGLE_COMPONENT_VOLATILE: |
| case DEMANGLE_COMPONENT_VOLATILE_THIS: |
| case DEMANGLE_COMPONENT_CONST_THIS: |
| case DEMANGLE_COMPONENT_RESTRICT_THIS: |
| case DEMANGLE_COMPONENT_POINTER: |
| case DEMANGLE_COMPONENT_REFERENCE: |
| case DEMANGLE_COMPONENT_RVALUE_REFERENCE: |
| replace_typedefs (info, d_left (ret_comp), finder, data); |
| break; |
| |
| default: |
| break; |
| } |
| } |
| } |
| |
| /* Parse STRING and convert it to canonical form, resolving any |
| typedefs. If parsing fails, or if STRING is already canonical, |
| return nullptr. Otherwise return the canonical form. If |
| FINDER is not NULL, then type components are passed to FINDER to be |
| looked up. DATA is passed verbatim to FINDER. */ |
| |
| gdb::unique_xmalloc_ptr<char> |
| cp_canonicalize_string_full (const char *string, |
| canonicalization_ftype *finder, |
| void *data) |
| { |
| unsigned int estimated_len; |
| std::unique_ptr<demangle_parse_info> info; |
| |
| estimated_len = strlen (string) * 2; |
| info = cp_demangled_name_to_comp (string, NULL); |
| if (info != NULL) |
| { |
| /* Replace all the typedefs in the tree. */ |
| replace_typedefs (info.get (), info->tree, finder, data); |
| |
| /* Convert the tree back into a string. */ |
| gdb::unique_xmalloc_ptr<char> us = cp_comp_to_string (info->tree, |
| estimated_len); |
| gdb_assert (us); |
| |
| /* Finally, compare the original string with the computed |
| name, returning NULL if they are the same. */ |
| if (strcmp (us.get (), string) == 0) |
| return nullptr; |
| |
| return us; |
| } |
| |
| return nullptr; |
| } |
| |
| /* Like cp_canonicalize_string_full, but always passes NULL for |
| FINDER. */ |
| |
| gdb::unique_xmalloc_ptr<char> |
| cp_canonicalize_string_no_typedefs (const char *string) |
| { |
| return cp_canonicalize_string_full (string, NULL, NULL); |
| } |
| |
| /* Parse STRING and convert it to canonical form. If parsing fails, |
| or if STRING is already canonical, return nullptr. |
| Otherwise return the canonical form. */ |
| |
| gdb::unique_xmalloc_ptr<char> |
| cp_canonicalize_string (const char *string) |
| { |
| std::unique_ptr<demangle_parse_info> info; |
| unsigned int estimated_len; |
| |
| if (cp_already_canonical (string)) |
| return nullptr; |
| |
| info = cp_demangled_name_to_comp (string, NULL); |
| if (info == NULL) |
| return nullptr; |
| |
| estimated_len = strlen (string) * 2; |
| gdb::unique_xmalloc_ptr<char> us (cp_comp_to_string (info->tree, |
| estimated_len)); |
| |
| if (!us) |
| { |
| warning (_("internal error: string \"%s\" failed to be canonicalized"), |
| string); |
| return nullptr; |
| } |
| |
| if (strcmp (us.get (), string) == 0) |
| return nullptr; |
| |
| return us; |
| } |
| |
| /* 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 std::unique_ptr<demangle_parse_info> |
| mangled_name_to_comp (const char *mangled_name, int options, |
| void **memory, |
| gdb::unique_xmalloc_ptr<char> *demangled_p) |
| { |
| /* If it looks like a v3 mangled name, then try to go directly |
| to trees. */ |
| if (mangled_name[0] == '_' && mangled_name[1] == 'Z') |
| { |
| struct demangle_component *ret; |
| |
| ret = cplus_demangle_v3_components (mangled_name, |
| options, memory); |
| if (ret) |
| { |
| std::unique_ptr<demangle_parse_info> info (new demangle_parse_info); |
| info->tree = ret; |
| *demangled_p = NULL; |
| return info; |
| } |
| } |
| |
| /* If it doesn't, or if that failed, then try to demangle the |
| name. */ |
| gdb::unique_xmalloc_ptr<char> demangled_name = gdb_demangle (mangled_name, |
| options); |
| if (demangled_name == NULL) |
| return NULL; |
| |
| /* If we could demangle the name, parse it to build the component |
| tree. */ |
| std::unique_ptr<demangle_parse_info> info |
| = cp_demangled_name_to_comp (demangled_name.get (), NULL); |
| |
| if (info == NULL) |
| return NULL; |
| |
| *demangled_p = std::move (demangled_name); |
| return info; |
| } |
| |
| /* Return the name of the class containing method PHYSNAME. */ |
| |
| char * |
| cp_class_name_from_physname (const char *physname) |
| { |
| void *storage = NULL; |
| gdb::unique_xmalloc_ptr<char> demangled_name; |
| gdb::unique_xmalloc_ptr<char> ret; |
| struct demangle_component *ret_comp, *prev_comp, *cur_comp; |
| std::unique_ptr<demangle_parse_info> info; |
| int done; |
| |
| info = mangled_name_to_comp (physname, DMGL_ANSI, |
| &storage, &demangled_name); |
| if (info == NULL) |
| return NULL; |
| |
| done = 0; |
| ret_comp = info->tree; |
| |
| /* First strip off any qualifiers, if we have a function or |
| method. */ |
| while (!done) |
| switch (ret_comp->type) |
| { |
| 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; |
| default: |
| done = 1; |
| break; |
| } |
| |
| /* If what we have now is a function, discard the argument list. */ |
| if (ret_comp->type == DEMANGLE_COMPONENT_TYPED_NAME) |
| ret_comp = d_left (ret_comp); |
| |
| /* If what we have now is a template, strip off the template |
| arguments. The left subtree may be a qualified name. */ |
| if (ret_comp->type == DEMANGLE_COMPONENT_TEMPLATE) |
| ret_comp = d_left (ret_comp); |
| |
| /* What we have now should be a name, possibly qualified. |
| Additional qualifiers could live in the left subtree or the right |
| subtree. Find the last piece. */ |
| done = 0; |
| prev_comp = NULL; |
| cur_comp = ret_comp; |
| while (!done) |
| switch (cur_comp->type) |
| { |
| case DEMANGLE_COMPONENT_QUAL_NAME: |
| case DEMANGLE_COMPONENT_LOCAL_NAME: |
| prev_comp = cur_comp; |
| cur_comp = d_right (cur_comp); |
| break; |
| case DEMANGLE_COMPONENT_TEMPLATE: |
| case DEMANGLE_COMPONENT_NAME: |
| case DEMANGLE_COMPONENT_CTOR: |
| case DEMANGLE_COMPONENT_DTOR: |
| case DEMANGLE_COMPONENT_OPERATOR: |
| case DEMANGLE_COMPONENT_EXTENDED_OPERATOR: |
| done = 1; |
| break; |
| default: |
| done = 1; |
| cur_comp = NULL; |
| break; |
| } |
| |
| if (cur_comp != NULL && prev_comp != NULL) |
| { |
| /* We want to discard the rightmost child of PREV_COMP. */ |
| *prev_comp = *d_left (prev_comp); |
| /* The ten is completely arbitrary; we don't have a good |
| estimate. */ |
| ret = cp_comp_to_string (ret_comp, 10); |
| } |
| |
| xfree (storage); |
| return ret.release (); |
| } |
| |
| /* Return the child of COMP which is the basename of a method, |
| variable, et cetera. All scope qualifiers are discarded, but |
| template arguments will be included. The component tree may be |
| modified. */ |
| |
| static struct demangle_component * |
| unqualified_name_from_comp (struct demangle_component *comp) |
| { |
| struct demangle_component *ret_comp = comp, *last_template; |
| int done; |
| |
| done = 0; |
| last_template = NULL; |
| while (!done) |
| switch (ret_comp->type) |
| { |
| case DEMANGLE_COMPONENT_QUAL_NAME: |
| case DEMANGLE_COMPONENT_LOCAL_NAME: |
| ret_comp = d_right (ret_comp); |
| break; |
| case DEMANGLE_COMPONENT_TYPED_NAME: |
| ret_comp = d_left (ret_comp); |
| break; |
| case DEMANGLE_COMPONENT_TEMPLATE: |
| gdb_assert (last_template == NULL); |
| last_template = ret_comp; |
| ret_comp = d_left (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_CTOR: |
| case DEMANGLE_COMPONENT_DTOR: |
| case DEMANGLE_COMPONENT_OPERATOR: |
| case DEMANGLE_COMPONENT_EXTENDED_OPERATOR: |
| done = 1; |
| break; |
| default: |
| return NULL; |
| break; |
| } |
| |
| if (last_template) |
| { |
| d_left (last_template) = ret_comp; |
| return last_template; |
| } |
| |
| return ret_comp; |
| } |
| |
| /* Return the name of the method whose linkage name is PHYSNAME. */ |
| |
| char * |
| method_name_from_physname (const char *physname) |
| { |
| void *storage = NULL; |
| gdb::unique_xmalloc_ptr<char> demangled_name; |
| gdb::unique_xmalloc_ptr<char> ret; |
| struct demangle_component *ret_comp; |
| std::unique_ptr<demangle_parse_info> info; |
| |
| info = mangled_name_to_comp (physname, DMGL_ANSI, |
| &storage, &demangled_name); |
| if (info == NULL) |
| return NULL; |
| |
| ret_comp = unqualified_name_from_comp (info->tree); |
| |
| 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); |
| return ret.release (); |
| } |
| |
| /* If FULL_NAME is the demangled name of a C++ function (including an |
| arg list, possibly including namespace/class qualifications), |
| return a new string containing only the function name (without the |
| arg list/class qualifications). Otherwise, return NULL. */ |
| |
| gdb::unique_xmalloc_ptr<char> |
| cp_func_name (const char *full_name) |
| { |
| gdb::unique_xmalloc_ptr<char> ret; |
| struct demangle_component *ret_comp; |
| std::unique_ptr<demangle_parse_info> info; |
| |
| info = cp_demangled_name_to_comp (full_name, NULL); |
| if (!info) |
| return nullptr; |
| |
| ret_comp = unqualified_name_from_comp (info->tree); |
| |
| if (ret_comp != NULL) |
| ret = cp_comp_to_string (ret_comp, 10); |
| |
| return ret; |
| } |
| |
| /* Helper for cp_remove_params. DEMANGLED_NAME is the name of a |
| function, including parameters and (optionally) a return type. |
| Return the name of the function without parameters or return type, |
| or NULL if we can not parse the name. If REQUIRE_PARAMS is false, |
| then tolerate a non-existing or unbalanced parameter list. */ |
| |
| static gdb::unique_xmalloc_ptr<char> |
| cp_remove_params_1 (const char *demangled_name, bool require_params) |
| { |
| bool done = false; |
| struct demangle_component *ret_comp; |
| std::unique_ptr<demangle_parse_info> info; |
| gdb::unique_xmalloc_ptr<char> ret; |
| |
| if (demangled_name == NULL) |
| return NULL; |
| |
| info = cp_demangled_name_to_comp (demangled_name, NULL); |
| if (info == NULL) |
| return NULL; |
| |
| /* First strip off any qualifiers, if we have a function or method. */ |
| ret_comp = info->tree; |
| while (!done) |
| switch (ret_comp->type) |
| { |
| 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; |
| default: |
| done = true; |
| break; |
| } |
| |
| /* What we have now should be a function. Return its name. */ |
| if (ret_comp->type == DEMANGLE_COMPONENT_TYPED_NAME) |
| ret = cp_comp_to_string (d_left (ret_comp), 10); |
| else if (!require_params |
| && (ret_comp->type == DEMANGLE_COMPONENT_NAME |
| || ret_comp->type == DEMANGLE_COMPONENT_QUAL_NAME |
| || ret_comp->type == DEMANGLE_COMPONENT_TEMPLATE)) |
| ret = cp_comp_to_string (ret_comp, 10); |
| |
| return ret; |
| } |
| |
| /* DEMANGLED_NAME is the name of a function, including parameters and |
| (optionally) a return type. Return the name of the function |
| without parameters or return type, or NULL if we can not parse the |
| name. */ |
| |
| gdb::unique_xmalloc_ptr<char> |
| cp_remove_params (const char *demangled_name) |
| { |
| return cp_remove_params_1 (demangled_name, true); |
| } |
| |
| /* See cp-support.h. */ |
| |
| gdb::unique_xmalloc_ptr<char> |
| cp_remove_params_if_any (const char *demangled_name, bool completion_mode) |
| { |
| /* Trying to remove parameters from the empty string fails. If |
| we're completing / matching everything, avoid returning NULL |
| which would make callers interpret the result as an error. */ |
| if (demangled_name[0] == '\0' && completion_mode) |
| return make_unique_xstrdup (""); |
| |
| gdb::unique_xmalloc_ptr<char> without_params |
| = cp_remove_params_1 (demangled_name, false); |
| |
| if (without_params == NULL && completion_mode) |
| { |
| std::string copy = demangled_name; |
| |
| while (!copy.empty ()) |
| { |
| copy.pop_back (); |
| without_params = cp_remove_params_1 (copy.c_str (), false); |
| if (without_params != NULL) |
| break; |
| } |
| } |
| |
| return without_params; |
| } |
| |
| /* Here are some random pieces of trivia to keep in mind while trying |
| to take apart demangled names: |
| |
| - 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? */ |
| |
| |
| /* This returns the length of first component of NAME, which should be |
| the demangled name of a C++ variable/function/method/etc. |
| Specifically, it returns the index of the first colon forming the |
| boundary of the first component: so, given 'A::foo' or 'A::B::foo' |
| it returns the 1, and given 'foo', it returns 0. */ |
| |
| /* The character in NAME indexed by the return value is guaranteed to |
| always be either ':' or '\0'. */ |
| |
| /* NOTE: carlton/2003-03-13: This function is currently only intended |
| for internal use: it's probably not entirely safe when called on |
| user-generated input, because some of the 'index += 2' lines in |
| cp_find_first_component_aux might go past the end of malformed |
| input. */ |
| |
| unsigned int |
| cp_find_first_component (const char *name) |
| { |
| return cp_find_first_component_aux (name, 0); |
| } |
| |
| /* Helper function for cp_find_first_component. Like that function, |
| it returns the length of the first component of NAME, but to make |
| the recursion easier, it also stops if it reaches an unexpected ')' |
| or '>' if the value of PERMISSIVE is nonzero. */ |
| |
| static unsigned int |
| cp_find_first_component_aux (const char *name, int permissive) |
| { |
| unsigned int index = 0; |
| /* Operator names can show up in unexpected places. Since these can |
| contain parentheses or angle brackets, they can screw up the |
| recursion. But not every string 'operator' is part of an |
| operator name: e.g. you could have a variable 'cooperator'. So |
| this variable tells us whether or not we should treat the string |
| 'operator' as starting an operator. */ |
| int operator_possible = 1; |
| |
| for (;; ++index) |
| { |
| switch (name[index]) |
| { |
| case '<': |
| /* Template; eat it up. The calls to cp_first_component |
| should only return (I hope!) when they reach the '>' |
| terminating the component or a '::' between two |
| components. (Hence the '+ 2'.) */ |
| index += 1; |
| for (index += cp_find_first_component_aux (name + index, 1); |
| name[index] != '>'; |
| index += cp_find_first_component_aux (name + index, 1)) |
| { |
| if (name[index] != ':') |
| { |
| demangled_name_complaint (name); |
| return strlen (name); |
| } |
| index += 2; |
| } |
| operator_possible = 1; |
| break; |
| case '(': |
| /* Similar comment as to '<'. */ |
| index += 1; |
| for (index += cp_find_first_component_aux (name + index, 1); |
| name[index] != ')'; |
| index += cp_find_first_component_aux (name + index, 1)) |
| { |
| if (name[index] != ':') |
| { |
| demangled_name_complaint (name); |
| return strlen (name); |
| } |
| index += 2; |
| } |
| operator_possible = 1; |
| break; |
| case '>': |
| case ')': |
| if (permissive) |
| return index; |
| else |
| { |
| demangled_name_complaint (name); |
| return strlen (name); |
| } |
| case '\0': |
| return index; |
| case ':': |
| /* ':' marks a component iff the next character is also a ':'. |
| Otherwise it is probably malformed input. */ |
| if (name[index + 1] == ':') |
| return index; |
| break; |
| case 'o': |
| /* Operator names can screw up the recursion. */ |
| if (operator_possible |
| && startswith (name + index, CP_OPERATOR_STR)) |
| { |
| index += CP_OPERATOR_LEN; |
| while (ISSPACE(name[index])) |
| ++index; |
| switch (name[index]) |
| { |
| case '\0': |
| return index; |
| /* Skip over one less than the appropriate number of |
| characters: the for loop will skip over the last |
| one. */ |
| case '<': |
| if (name[index + 1] == '<') |
| index += 1; |
| else |
| index += 0; |
| break; |
| case '>': |
| case '-': |
| if (name[index + 1] == '>') |
| index += 1; |
| else |
| index += 0; |
| break; |
| case '(': |
| index += 1; |
| break; |
| default: |
| index += 0; |
| break; |
| } |
| } |
| operator_possible = 0; |
| break; |
| case ' ': |
| case ',': |
| case '.': |
| case '&': |
| case '*': |
| /* NOTE: carlton/2003-04-18: I'm not sure what the precise |
| set of relevant characters are here: it's necessary to |
| include any character that can show up before 'operator' |
| in a demangled name, and it's safe to include any |
| character that can't be part of an identifier's name. */ |
| operator_possible = 1; |
| break; |
| default: |
| operator_possible = 0; |
| break; |
| } |
| } |
| } |
| |
| /* Complain about a demangled name that we don't know how to parse. |
| NAME is the demangled name in question. */ |
| |
| static void |
| demangled_name_complaint (const char *name) |
| { |
| complaint ("unexpected demangled name '%s'", name); |
| } |
| |
| /* If NAME is the fully-qualified name of a C++ |
| function/variable/method/etc., this returns the length of its |
| entire prefix: all of the namespaces and classes that make up its |
| name. Given 'A::foo', it returns 1, given 'A::B::foo', it returns |
| 4, given 'foo', it returns 0. */ |
| |
| unsigned int |
| cp_entire_prefix_len (const char *name) |
| { |
| unsigned int current_len = cp_find_first_component (name); |
| unsigned int previous_len = 0; |
| |
| while (name[current_len] != '\0') |
| { |
| gdb_assert (name[current_len] == ':'); |
| previous_len = current_len; |
| /* Skip the '::'. */ |
| current_len += 2; |
| current_len += cp_find_first_component (name + current_len); |
| } |
| |
| return previous_len; |
| } |
| |
| /* Overload resolution functions. */ |
| |
| /* Test to see if SYM is a symbol that we haven't seen corresponding |
| to a function named OLOAD_NAME. If so, add it to |
| OVERLOAD_LIST. */ |
| |
| static void |
| overload_list_add_symbol (struct symbol *sym, |
| const char *oload_name, |
| std::vector<symbol *> *overload_list) |
| { |
| /* If there is no type information, we can't do anything, so |
| skip. */ |
| if (sym->type () == NULL) |
| return; |
| |
| /* skip any symbols that we've already considered. */ |
| for (symbol *listed_sym : *overload_list) |
| if (strcmp (sym->linkage_name (), listed_sym->linkage_name ()) == 0) |
| return; |
| |
| /* Get the demangled name without parameters */ |
| gdb::unique_xmalloc_ptr<char> sym_name |
| = cp_remove_params (sym->natural_name ()); |
| if (!sym_name) |
| return; |
| |
| /* skip symbols that cannot match */ |
| if (strcmp (sym_name.get (), oload_name) != 0) |
| return; |
| |
| overload_list->push_back (sym); |
| } |
| |
| /* Return a null-terminated list of pointers to function symbols that |
| are named FUNC_NAME and are visible within NAMESPACE. */ |
| |
| struct std::vector<symbol *> |
| make_symbol_overload_list (const char *func_name, |
| const char *the_namespace) |
| { |
| const char *name; |
| std::vector<symbol *> overload_list; |
| |
| overload_list.reserve (100); |
| |
| add_symbol_overload_list_using (func_name, the_namespace, &overload_list); |
| |
| if (the_namespace[0] == '\0') |
| name = func_name; |
| else |
| { |
| char *concatenated_name |
| = (char *) alloca (strlen (the_namespace) + 2 + strlen (func_name) + 1); |
| strcpy (concatenated_name, the_namespace); |
| strcat (concatenated_name, "::"); |
| strcat (concatenated_name, func_name); |
| name = concatenated_name; |
| } |
| |
| add_symbol_overload_list_qualified (name, &overload_list); |
| return overload_list; |
| } |
| |
| /* Add all symbols with a name matching NAME in BLOCK to the overload |
| list. */ |
| |
| static void |
| add_symbol_overload_list_block (const char *name, |
| const struct block *block, |
| std::vector<symbol *> *overload_list) |
| { |
| struct block_iterator iter; |
| struct symbol *sym; |
| |
| lookup_name_info lookup_name (name, symbol_name_match_type::FULL); |
| |
| ALL_BLOCK_SYMBOLS_WITH_NAME (block, lookup_name, iter, sym) |
| overload_list_add_symbol (sym, name, overload_list); |
| } |
| |
| /* Adds the function FUNC_NAME from NAMESPACE to the overload set. */ |
| |
| static void |
| add_symbol_overload_list_namespace (const char *func_name, |
| const char *the_namespace, |
| std::vector<symbol *> *overload_list) |
| { |
| const char *name; |
| const struct block *block = NULL; |
| |
| if (the_namespace[0] == '\0') |
| name = func_name; |
| else |
| { |
| char *concatenated_name |
| = (char *) alloca (strlen (the_namespace) + 2 + strlen (func_name) + 1); |
| |
| strcpy (concatenated_name, the_namespace); |
| strcat (concatenated_name, "::"); |
| strcat (concatenated_name, func_name); |
| name = concatenated_name; |
| } |
| |
| /* Look in the static block. */ |
| block = block_static_block (get_selected_block (0)); |
| if (block) |
| add_symbol_overload_list_block (name, block, overload_list); |
| |
| /* Look in the global block. */ |
| block = block_global_block (block); |
| if (block) |
| add_symbol_overload_list_block (name, block, overload_list); |
| |
| } |
| |
| /* Search the namespace of the given type and namespace of and public |
| base types. */ |
| |
| static void |
| add_symbol_overload_list_adl_namespace (struct type *type, |
| const char *func_name, |
| std::vector<symbol *> *overload_list) |
| { |
| char *the_namespace; |
| const char *type_name; |
| int i, prefix_len; |
| |
| while (type->is_pointer_or_reference () |
| || type->code () == TYPE_CODE_ARRAY |
| || type->code () == TYPE_CODE_TYPEDEF) |
| { |
| if (type->code () == TYPE_CODE_TYPEDEF) |
| type = check_typedef (type); |
| else |
| type = TYPE_TARGET_TYPE (type); |
| } |
| |
| type_name = type->name (); |
| |
| if (type_name == NULL) |
| return; |
| |
| prefix_len = cp_entire_prefix_len (type_name); |
| |
| if (prefix_len != 0) |
| { |
| the_namespace = (char *) alloca (prefix_len + 1); |
| strncpy (the_namespace, type_name, prefix_len); |
| the_namespace[prefix_len] = '\0'; |
| |
| add_symbol_overload_list_namespace (func_name, the_namespace, |
| overload_list); |
| } |
| |
| /* Check public base type */ |
| if (type->code () == TYPE_CODE_STRUCT) |
| for (i = 0; i < TYPE_N_BASECLASSES (type); i++) |
| { |
| if (BASETYPE_VIA_PUBLIC (type, i)) |
| add_symbol_overload_list_adl_namespace (TYPE_BASECLASS (type, i), |
| func_name, |
| overload_list); |
| } |
| } |
| |
| /* Adds to OVERLOAD_LIST the overload list overload candidates for |
| FUNC_NAME found through argument dependent lookup. */ |
| |
| void |
| add_symbol_overload_list_adl (gdb::array_view<type *> arg_types, |
| const char *func_name, |
| std::vector<symbol *> *overload_list) |
| { |
| for (type *arg_type : arg_types) |
| add_symbol_overload_list_adl_namespace (arg_type, func_name, |
| overload_list); |
| } |
| |
| /* This applies the using directives to add namespaces to search in, |
| and then searches for overloads in all of those namespaces. It |
| adds the symbols found to sym_return_val. Arguments are as in |
| make_symbol_overload_list. */ |
| |
| static void |
| add_symbol_overload_list_using (const char *func_name, |
| const char *the_namespace, |
| std::vector<symbol *> *overload_list) |
| { |
| struct using_direct *current; |
| const struct block *block; |
| |
| /* First, go through the using directives. If any of them apply, |
| look in the appropriate namespaces for new functions to match |
| on. */ |
| |
| for (block = get_selected_block (0); |
| block != NULL; |
| block = BLOCK_SUPERBLOCK (block)) |
| for (current = block_using (block); |
| current != NULL; |
| current = current->next) |
| { |
| /* Prevent recursive calls. */ |
| if (current->searched) |
| continue; |
| |
| /* If this is a namespace alias or imported declaration ignore |
| it. */ |
| if (current->alias != NULL || current->declaration != NULL) |
| continue; |
| |
| if (strcmp (the_namespace, current->import_dest) == 0) |
| { |
| /* Mark this import as searched so that the recursive call |
| does not search it again. */ |
| scoped_restore reset_directive_searched |
| = make_scoped_restore (¤t->searched, 1); |
| |
| add_symbol_overload_list_using (func_name, |
| current->import_src, |
| overload_list); |
| } |
| } |
| |
| /* Now, add names for this namespace. */ |
| add_symbol_overload_list_namespace (func_name, the_namespace, |
| overload_list); |
| } |
| |
| /* This does the bulk of the work of finding overloaded symbols. |
| FUNC_NAME is the name of the overloaded function we're looking for |
| (possibly including namespace info). */ |
| |
| static void |
| add_symbol_overload_list_qualified (const char *func_name, |
| std::vector<symbol *> *overload_list) |
| { |
| const struct block *b, *surrounding_static_block = 0; |
| |
| /* Look through the partial symtabs for all symbols which begin by |
| matching FUNC_NAME. Make sure we read that symbol table in. */ |
| |
| for (objfile *objf : current_program_space->objfiles ()) |
| objf->expand_symtabs_for_function (func_name); |
| |
| /* Search upwards from currently selected frame (so that we can |
| complete on local vars. */ |
| |
| for (b = get_selected_block (0); b != NULL; b = BLOCK_SUPERBLOCK (b)) |
| add_symbol_overload_list_block (func_name, b, overload_list); |
| |
| surrounding_static_block = block_static_block (get_selected_block (0)); |
| |
| /* Go through the symtabs and check the externs and statics for |
| symbols which match. */ |
| |
| for (objfile *objfile : current_program_space->objfiles ()) |
| { |
| for (compunit_symtab *cust : objfile->compunits ()) |
| { |
| QUIT; |
| b = BLOCKVECTOR_BLOCK (cust->blockvector (), GLOBAL_BLOCK); |
| add_symbol_overload_list_block (func_name, b, overload_list); |
| } |
| } |
| |
| for (objfile *objfile : current_program_space->objfiles ()) |
| { |
| for (compunit_symtab *cust : objfile->compunits ()) |
| { |
| QUIT; |
| b = BLOCKVECTOR_BLOCK (cust->blockvector (), STATIC_BLOCK); |
| /* Don't do this block twice. */ |
| if (b == surrounding_static_block) |
| continue; |
| add_symbol_overload_list_block (func_name, b, overload_list); |
| } |
| } |
| } |
| |
| /* Lookup the rtti type for a class name. */ |
| |
| struct type * |
| cp_lookup_rtti_type (const char *name, const struct block *block) |
| { |
| struct symbol * rtti_sym; |
| struct type * rtti_type; |
| |
| /* Use VAR_DOMAIN here as NAME may be a typedef. PR 18141, 18417. |
| Classes "live" in both STRUCT_DOMAIN and VAR_DOMAIN. */ |
| rtti_sym = lookup_symbol (name, block, VAR_DOMAIN, NULL).symbol; |
| |
| if (rtti_sym == NULL) |
| { |
| warning (_("RTTI symbol not found for class '%s'"), name); |
| return NULL; |
| } |
| |
| if (rtti_sym->aclass () != LOC_TYPEDEF) |
| { |
| warning (_("RTTI symbol for class '%s' is not a type"), name); |
| return NULL; |
| } |
| |
| rtti_type = check_typedef (rtti_sym->type ()); |
| |
| switch (rtti_type->code ()) |
| { |
| case TYPE_CODE_STRUCT: |
| break; |
| case TYPE_CODE_NAMESPACE: |
| /* chastain/2003-11-26: the symbol tables often contain fake |
| symbols for namespaces with the same name as the struct. |
| This warning is an indication of a bug in the lookup order |
| or a bug in the way that the symbol tables are populated. */ |
| warning (_("RTTI symbol for class '%s' is a namespace"), name); |
| return NULL; |
| default: |
| warning (_("RTTI symbol for class '%s' has bad type"), name); |
| return NULL; |
| } |
| |
| return rtti_type; |
| } |
| |
| #ifdef HAVE_WORKING_FORK |
| |
| /* If true, attempt to catch crashes in the demangler and print |
| useful debugging information. */ |
| |
| static bool catch_demangler_crashes = true; |
| |
| /* Stack context and environment for demangler crash recovery. */ |
| |
| static thread_local SIGJMP_BUF *gdb_demangle_jmp_buf; |
| |
| /* If true, attempt to dump core from the signal handler. */ |
| |
| static std::atomic<bool> gdb_demangle_attempt_core_dump; |
| |
| /* Signal handler for gdb_demangle. */ |
| |
| static void |
| gdb_demangle_signal_handler (int signo) |
| { |
| if (gdb_demangle_attempt_core_dump) |
| { |
| if (fork () == 0) |
| dump_core (); |
| |
| gdb_demangle_attempt_core_dump = false; |
| } |
| |
| SIGLONGJMP (*gdb_demangle_jmp_buf, signo); |
| } |
| |
| /* A helper for gdb_demangle that reports a demangling failure. */ |
| |
| static void |
| report_failed_demangle (const char *name, bool core_dump_allowed, |
| int crash_signal) |
| { |
| static bool error_reported = false; |
| |
| if (!error_reported) |
| { |
| std::string short_msg |
| = string_printf (_("unable to demangle '%s' " |
| "(demangler failed with signal %d)"), |
| name, crash_signal); |
| |
| std::string long_msg |
| = string_printf ("%s:%d: %s: %s", __FILE__, __LINE__, |
| "demangler-warning", short_msg.c_str ()); |
| |
| target_terminal::scoped_restore_terminal_state term_state; |
| target_terminal::ours_for_output (); |
| |
| begin_line (); |
| if (core_dump_allowed) |
| fprintf_unfiltered (gdb_stderr, |
| _("%s\nAttempting to dump core.\n"), |
| long_msg.c_str ()); |
| else |
| warn_cant_dump_core (long_msg.c_str ()); |
| |
| demangler_warning (__FILE__, __LINE__, "%s", short_msg.c_str ()); |
| |
| error_reported = true; |
| } |
| } |
| |
| #endif |
| |
| /* A wrapper for bfd_demangle. */ |
| |
| gdb::unique_xmalloc_ptr<char> |
| gdb_demangle (const char *name, int options) |
| { |
| gdb::unique_xmalloc_ptr<char> result; |
| int crash_signal = 0; |
| |
| #ifdef HAVE_WORKING_FORK |
| scoped_segv_handler_restore restore_segv |
| (catch_demangler_crashes |
| ? gdb_demangle_signal_handler |
| : nullptr); |
| |
| bool core_dump_allowed = gdb_demangle_attempt_core_dump; |
| SIGJMP_BUF jmp_buf; |
| scoped_restore restore_jmp_buf |
| = make_scoped_restore (&gdb_demangle_jmp_buf, &jmp_buf); |
| if (catch_demangler_crashes) |
| { |
| /* The signal handler may keep the signal blocked when we longjmp out |
| of it. If we have sigprocmask, we can use it to unblock the signal |
| afterwards and we can avoid the performance overhead of saving the |
| signal mask just in case the signal gets triggered. Otherwise, just |
| tell sigsetjmp to save the mask. */ |
| #ifdef HAVE_SIGPROCMASK |
| crash_signal = SIGSETJMP (*gdb_demangle_jmp_buf, 0); |
| #else |
| crash_signal = SIGSETJMP (*gdb_demangle_jmp_buf, 1); |
| #endif |
| } |
| #endif |
| |
| if (crash_signal == 0) |
| result.reset (bfd_demangle (NULL, name, options)); |
| |
| #ifdef HAVE_WORKING_FORK |
| if (catch_demangler_crashes) |
| { |
| if (crash_signal != 0) |
| { |
| #ifdef HAVE_SIGPROCMASK |
| /* If we got the signal, SIGSEGV may still be blocked; restore it. */ |
| sigset_t segv_sig_set; |
| sigemptyset (&segv_sig_set); |
| sigaddset (&segv_sig_set, SIGSEGV); |
| gdb_sigmask (SIG_UNBLOCK, &segv_sig_set, NULL); |
| #endif |
| |
| /* If there was a failure, we can't report it here, because |
| we might be in a background thread. Instead, arrange for |
| the reporting to happen on the main thread. */ |
| std::string copy = name; |
| run_on_main_thread ([=] () |
| { |
| report_failed_demangle (copy.c_str (), core_dump_allowed, |
| crash_signal); |
| }); |
| |
| result = NULL; |
| } |
| } |
| #endif |
| |
| return result; |
| } |
| |
| /* See cp-support.h. */ |
| |
| unsigned int |
| cp_search_name_hash (const char *search_name) |
| { |
| /* cp_entire_prefix_len assumes a fully-qualified name with no |
| leading "::". */ |
| if (startswith (search_name, "::")) |
| search_name += 2; |
| |
| unsigned int prefix_len = cp_entire_prefix_len (search_name); |
| if (prefix_len != 0) |
| search_name += prefix_len + 2; |
| |
| unsigned int hash = 0; |
| for (const char *string = search_name; *string != '\0'; ++string) |
| { |
| string = skip_spaces (string); |
| |
| if (*string == '(') |
| break; |
| |
| /* Ignore ABI tags such as "[abi:cxx11]. */ |
| if (*string == '[' |
| && startswith (string + 1, "abi:") |
| && string[5] != ':') |
| break; |
| |
| /* Ignore template parameter lists. */ |
| if (string[0] == '<' |
| && string[1] != '(' && string[1] != '<' && string[1] != '=' |
| && string[1] != ' ' && string[1] != '\0') |
| break; |
| |
| hash = SYMBOL_HASH_NEXT (hash, *string); |
| } |
| return hash; |
| } |
| |
| /* Helper for cp_symbol_name_matches (i.e., symbol_name_matcher_ftype |
| implementation for symbol_name_match_type::WILD matching). Split |
| to a separate function for unit-testing convenience. |
| |
| If SYMBOL_SEARCH_NAME has more scopes than LOOKUP_NAME, we try to |
| match ignoring the extra leading scopes of SYMBOL_SEARCH_NAME. |
| This allows conveniently setting breakpoints on functions/methods |
| inside any namespace/class without specifying the fully-qualified |
| name. |
| |
| E.g., these match: |
| |
| [symbol search name] [lookup name] |
| foo::bar::func foo::bar::func |
| foo::bar::func bar::func |
| foo::bar::func func |
| |
| While these don't: |
| |
| [symbol search name] [lookup name] |
| foo::zbar::func bar::func |
| foo::bar::func foo::func |
| |
| See more examples in the test_cp_symbol_name_matches selftest |
| function below. |
| |
| See symbol_name_matcher_ftype for description of SYMBOL_SEARCH_NAME |
| and COMP_MATCH_RES. |
| |
| LOOKUP_NAME/LOOKUP_NAME_LEN is the name we're looking up. |
| |
| See strncmp_iw_with_mode for description of MODE. |
| */ |
| |
| static bool |
| cp_symbol_name_matches_1 (const char *symbol_search_name, |
| const char *lookup_name, |
| size_t lookup_name_len, |
| strncmp_iw_mode mode, |
| completion_match_result *comp_match_res) |
| { |
| const char *sname = symbol_search_name; |
| completion_match_for_lcd *match_for_lcd |
| = (comp_match_res != NULL ? &comp_match_res->match_for_lcd : NULL); |
| |
| while (true) |
| { |
| if (strncmp_iw_with_mode (sname, lookup_name, lookup_name_len, |
| mode, language_cplus, match_for_lcd, true) == 0) |
| { |
| if (comp_match_res != NULL) |
| { |
| /* Note here we set different MATCH and MATCH_FOR_LCD |
| strings. This is because with |
| |
| (gdb) b push_bac[TAB] |
| |
| we want the completion matches to list |
| |
| std::vector<int>::push_back(...) |
| std::vector<char>::push_back(...) |
| |
| etc., which are SYMBOL_SEARCH_NAMEs, while we want |
| the input line to auto-complete to |
| |
| (gdb) push_back(...) |
| |
| which is SNAME, not to |
| |
| (gdb) std::vector< |
| |
| which would be the regular common prefix between all |
| the matches otherwise. */ |
| comp_match_res->set_match (symbol_search_name, sname); |
| } |
| return true; |
| } |
| |
| unsigned int len = cp_find_first_component (sname); |
| |
| if (sname[len] == '\0') |
| return false; |
| |
| gdb_assert (sname[len] == ':'); |
| /* Skip the '::'. */ |
| sname += len + 2; |
| } |
| } |
| |
| /* C++ symbol_name_matcher_ftype implementation. */ |
| |
| static bool |
| cp_fq_symbol_name_matches (const char *symbol_search_name, |
| const lookup_name_info &lookup_name, |
| completion_match_result *comp_match_res) |
| { |
| /* Get the demangled name. */ |
| const std::string &name = lookup_name.cplus ().lookup_name (); |
| completion_match_for_lcd *match_for_lcd |
| = (comp_match_res != NULL ? &comp_match_res->match_for_lcd : NULL); |
| strncmp_iw_mode mode = (lookup_name.completion_mode () |
| ? strncmp_iw_mode::NORMAL |
| : strncmp_iw_mode::MATCH_PARAMS); |
| |
| if (strncmp_iw_with_mode (symbol_search_name, |
| name.c_str (), name.size (), |
| mode, language_cplus, match_for_lcd) == 0) |
| { |
| if (comp_match_res != NULL) |
| comp_match_res->set_match (symbol_search_name); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /* C++ symbol_name_matcher_ftype implementation for wild matches. |
| Defers work to cp_symbol_name_matches_1. */ |
| |
| static bool |
| cp_symbol_name_matches (const char *symbol_search_name, |
| const lookup_name_info &lookup_name, |
| completion_match_result *comp_match_res) |
| { |
| /* Get the demangled name. */ |
| const std::string &name = lookup_name.cplus ().lookup_name (); |
| |
| strncmp_iw_mode mode = (lookup_name.completion_mode () |
| ? strncmp_iw_mode::NORMAL |
| : strncmp_iw_mode::MATCH_PARAMS); |
| |
| return cp_symbol_name_matches_1 (symbol_search_name, |
| name.c_str (), name.size (), |
| mode, comp_match_res); |
| } |
| |
| /* See cp-support.h. */ |
| |
| symbol_name_matcher_ftype * |
| cp_get_symbol_name_matcher (const lookup_name_info &lookup_name) |
| { |
| switch (lookup_name.match_type ()) |
| { |
| case symbol_name_match_type::FULL: |
| case symbol_name_match_type::EXPRESSION: |
| case symbol_name_match_type::SEARCH_NAME: |
| return cp_fq_symbol_name_matches; |
| case symbol_name_match_type::WILD: |
| return cp_symbol_name_matches; |
| } |
| |
| gdb_assert_not_reached (""); |
| } |
| |
| #if GDB_SELF_TEST |
| |
| namespace selftests { |
| |
| static void |
| test_cp_symbol_name_matches () |
| { |
| #define CHECK_MATCH(SYMBOL, INPUT) \ |
| SELF_CHECK (cp_symbol_name_matches_1 (SYMBOL, \ |
| INPUT, sizeof (INPUT) - 1, \ |
| strncmp_iw_mode::MATCH_PARAMS, \ |
| NULL)) |
| |
| #define CHECK_NOT_MATCH(SYMBOL, INPUT) \ |
| SELF_CHECK (!cp_symbol_name_matches_1 (SYMBOL, \ |
| INPUT, sizeof (INPUT) - 1, \ |
| strncmp_iw_mode::MATCH_PARAMS, \ |
| NULL)) |
| |
| /* Like CHECK_MATCH, and also check that INPUT (and all substrings |
| that start at index 0) completes to SYMBOL. */ |
| #define CHECK_MATCH_C(SYMBOL, INPUT) \ |
| do \ |
| { \ |
| CHECK_MATCH (SYMBOL, INPUT); \ |
| for (size_t i = 0; i < sizeof (INPUT) - 1; i++) \ |
| SELF_CHECK (cp_symbol_name_matches_1 (SYMBOL, INPUT, i, \ |
| strncmp_iw_mode::NORMAL, \ |
| NULL)); \ |
| } while (0) |
| |
| /* Like CHECK_NOT_MATCH, and also check that INPUT does NOT complete |
| to SYMBOL. */ |
| #define CHECK_NOT_MATCH_C(SYMBOL, INPUT) \ |
| do \ |
| { \ |
| CHECK_NOT_MATCH (SYMBOL, INPUT); \ |
| SELF_CHECK (!cp_symbol_name_matches_1 (SYMBOL, INPUT, \ |
| sizeof (INPUT) - 1, \ |
| strncmp_iw_mode::NORMAL, \ |
| NULL)); \ |
| } while (0) |
| |
| /* Lookup name without parens matches all overloads. */ |
| CHECK_MATCH_C ("function()", "function"); |
| CHECK_MATCH_C ("function(int)", "function"); |
| |
| /* Check whitespace around parameters is ignored. */ |
| CHECK_MATCH_C ("function()", "function ()"); |
| CHECK_MATCH_C ("function ( )", "function()"); |
| CHECK_MATCH_C ("function ()", "function( )"); |
| CHECK_MATCH_C ("func(int)", "func( int )"); |
| CHECK_MATCH_C ("func(int)", "func ( int ) "); |
| CHECK_MATCH_C ("func ( int )", "func( int )"); |
| CHECK_MATCH_C ("func ( int )", "func ( int ) "); |
| |
| /* Check symbol name prefixes aren't incorrectly matched. */ |
| CHECK_NOT_MATCH ("func", "function"); |
| CHECK_NOT_MATCH ("function", "func"); |
| CHECK_NOT_MATCH ("function()", "func"); |
| |
| /* Check that if the lookup name includes parameters, only the right |
| overload matches. */ |
| CHECK_MATCH_C ("function(int)", "function(int)"); |
| CHECK_NOT_MATCH_C ("function(int)", "function()"); |
| |
| /* Check that whitespace within symbol names is not ignored. */ |
| CHECK_NOT_MATCH_C ("function", "func tion"); |
| CHECK_NOT_MATCH_C ("func__tion", "func_ _tion"); |
| CHECK_NOT_MATCH_C ("func11tion", "func1 1tion"); |
| |
| /* Check the converse, which can happen with template function, |
| where the return type is part of the demangled name. */ |
| CHECK_NOT_MATCH_C ("func tion", "function"); |
| CHECK_NOT_MATCH_C ("func1 1tion", "func11tion"); |
| CHECK_NOT_MATCH_C ("func_ _tion", "func__tion"); |
| |
| /* Within parameters too. */ |
| CHECK_NOT_MATCH_C ("func(param)", "func(par am)"); |
| |
| /* Check handling of whitespace around C++ operators. */ |
| CHECK_NOT_MATCH_C ("operator<<", "opera tor<<"); |
| CHECK_NOT_MATCH_C ("operator<<", "operator< <"); |
| CHECK_NOT_MATCH_C ("operator<<", "operator < <"); |
| CHECK_NOT_MATCH_C ("operator==", "operator= ="); |
| CHECK_NOT_MATCH_C ("operator==", "operator = ="); |
| CHECK_MATCH_C ("operator<<", "operator <<"); |
| CHECK_MATCH_C ("operator<<()", "operator <<"); |
| CHECK_NOT_MATCH_C ("operator<<()", "operator<<(int)"); |
| CHECK_NOT_MATCH_C ("operator<<(int)", "operator<<()"); |
| CHECK_MATCH_C ("operator==", "operator =="); |
| CHECK_MATCH_C ("operator==()", "operator =="); |
| CHECK_MATCH_C ("operator <<", "operator<<"); |
| CHECK_MATCH_C ("operator ==", "operator=="); |
| CHECK_MATCH_C ("operator bool", "operator bool"); |
| CHECK_MATCH_C ("operator bool ()", "operator bool"); |
| CHECK_MATCH_C ("operatorX<<", "operatorX < <"); |
| CHECK_MATCH_C ("Xoperator<<", "Xoperator < <"); |
| |
| CHECK_MATCH_C ("operator()(int)", "operator()(int)"); |
| CHECK_MATCH_C ("operator()(int)", "operator ( ) ( int )"); |
| CHECK_MATCH_C ("operator()<long>(int)", "operator ( ) < long > ( int )"); |
| /* The first "()" is not the parameter list. */ |
| CHECK_NOT_MATCH ("operator()(int)", "operator"); |
| |
| /* Misc user-defined operator tests. */ |
| |
| CHECK_NOT_MATCH_C ("operator/=()", "operator ^="); |
| /* Same length at end of input. */ |
| CHECK_NOT_MATCH_C ("operator>>", "operator[]"); |
| /* Same length but not at end of input. */ |
| CHECK_NOT_MATCH_C ("operator>>()", "operator[]()"); |
| |
| CHECK_MATCH_C ("base::operator char*()", "base::operator char*()"); |
| CHECK_MATCH_C ("base::operator char*()", "base::operator char * ()"); |
| CHECK_MATCH_C ("base::operator char**()", "base::operator char * * ()"); |
| CHECK_MATCH ("base::operator char**()", "base::operator char * *"); |
| CHECK_MATCH_C ("base::operator*()", "base::operator*()"); |
| CHECK_NOT_MATCH_C ("base::operator char*()", "base::operatorc"); |
| CHECK_NOT_MATCH ("base::operator char*()", "base::operator char"); |
| CHECK_NOT_MATCH ("base::operator char*()", "base::operat"); |
| |
| /* Check handling of whitespace around C++ scope operators. */ |
| CHECK_NOT_MATCH_C ("foo::bar", "foo: :bar"); |
| CHECK_MATCH_C ("foo::bar", "foo :: bar"); |
| CHECK_MATCH_C ("foo :: bar", "foo::bar"); |
| |
| CHECK_MATCH_C ("abc::def::ghi()", "abc::def::ghi()"); |
| CHECK_MATCH_C ("abc::def::ghi ( )", "abc::def::ghi()"); |
| CHECK_MATCH_C ("abc::def::ghi()", "abc::def::ghi ( )"); |
| CHECK_MATCH_C ("function()", "function()"); |
| CHECK_MATCH_C ("bar::function()", "bar::function()"); |
| |
| /* Wild matching tests follow. */ |
| |
| /* Tests matching symbols in some scope. */ |
| CHECK_MATCH_C ("foo::function()", "function"); |
| CHECK_MATCH_C ("foo::function(int)", "function"); |
| CHECK_MATCH_C ("foo::bar::function()", "function"); |
| CHECK_MATCH_C ("bar::function()", "bar::function"); |
| CHECK_MATCH_C ("foo::bar::function()", "bar::function"); |
| CHECK_MATCH_C ("foo::bar::function(int)", "bar::function"); |
| |
| /* Same, with parameters in the lookup name. */ |
| CHECK_MATCH_C ("foo::function()", "function()"); |
| CHECK_MATCH_C ("foo::bar::function()", "function()"); |
| CHECK_MATCH_C ("foo::function(int)", "function(int)"); |
| CHECK_MATCH_C ("foo::function()", "foo::function()"); |
| CHECK_MATCH_C ("foo::bar::function()", "bar::function()"); |
| CHECK_MATCH_C ("foo::bar::function(int)", "bar::function(int)"); |
| CHECK_MATCH_C ("bar::function()", "bar::function()"); |
| |
| CHECK_NOT_MATCH_C ("foo::bar::function(int)", "bar::function()"); |
| |
| CHECK_MATCH_C ("(anonymous namespace)::bar::function(int)", |
| "bar::function(int)"); |
| CHECK_MATCH_C ("foo::(anonymous namespace)::bar::function(int)", |
| "function(int)"); |
| |
| /* Lookup scope wider than symbol scope, should not match. */ |
| CHECK_NOT_MATCH_C ("function()", "bar::function"); |
| CHECK_NOT_MATCH_C ("function()", "bar::function()"); |
| |
| /* Explicit global scope doesn't match. */ |
| CHECK_NOT_MATCH_C ("foo::function()", "::function"); |
| CHECK_NOT_MATCH_C ("foo::function()", "::function()"); |
| CHECK_NOT_MATCH_C ("foo::function(int)", "::function()"); |
| CHECK_NOT_MATCH_C ("foo::function(int)", "::function(int)"); |
| |
| /* Test ABI tag matching/ignoring. */ |
| |
| /* If the symbol name has an ABI tag, but the lookup name doesn't, |
| then the ABI tag in the symbol name is ignored. */ |
| CHECK_MATCH_C ("function[abi:foo]()", "function"); |
| CHECK_MATCH_C ("function[abi:foo](int)", "function"); |
| CHECK_MATCH_C ("function[abi:foo]()", "function ()"); |
| CHECK_NOT_MATCH_C ("function[abi:foo]()", "function (int)"); |
| |
| CHECK_MATCH_C ("function[abi:foo]()", "function[abi:foo]"); |
| CHECK_MATCH_C ("function[abi:foo](int)", "function[abi:foo]"); |
| CHECK_MATCH_C ("function[abi:foo]()", "function[abi:foo] ()"); |
| CHECK_MATCH_C ("function[abi:foo][abi:bar]()", "function"); |
| CHECK_MATCH_C ("function[abi:foo][abi:bar](int)", "function"); |
| CHECK_MATCH_C ("function[abi:foo][abi:bar]()", "function[abi:foo]"); |
| CHECK_MATCH_C ("function[abi:foo][abi:bar](int)", "function[abi:foo]"); |
| CHECK_MATCH_C ("function[abi:foo][abi:bar]()", "function[abi:foo] ()"); |
| CHECK_NOT_MATCH_C ("function[abi:foo][abi:bar]()", "function[abi:foo] (int)"); |
| |
| CHECK_MATCH_C ("function [abi:foo][abi:bar] ( )", "function [abi:foo]"); |
| |
| /* If the symbol name does not have an ABI tag, while the lookup |
| name has one, then there's no match. */ |
| CHECK_NOT_MATCH_C ("function()", "function[abi:foo]()"); |
| CHECK_NOT_MATCH_C ("function()", "function[abi:foo]"); |
| } |
| |
| /* If non-NULL, return STR wrapped in quotes. Otherwise, return a |
| "<null>" string (with no quotes). */ |
| |
| static std::string |
| quote (const char *str) |
| { |
| if (str != NULL) |
| return std::string (1, '"') + str + '"'; |
| else |
| return "<null>"; |
| } |
| |
| /* Check that removing parameter info out of NAME produces EXPECTED. |
| COMPLETION_MODE indicates whether we're testing normal and |
| completion mode. FILE and LINE are used to provide better test |
| location information in case ithe check fails. */ |
| |
| static void |
| check_remove_params (const char *file, int line, |
| const char *name, const char *expected, |
| bool completion_mode) |
| { |
| gdb::unique_xmalloc_ptr<char> result |
| = cp_remove_params_if_any (name, completion_mode); |
| |
| if ((expected == NULL) != (result == NULL) |
| || (expected != NULL |
| && strcmp (result.get (), expected) != 0)) |
| { |
| error (_("%s:%d: make-paramless self-test failed: (completion=%d) " |
| "\"%s\" -> %s, expected %s"), |
| file, line, completion_mode, name, |
| quote (result.get ()).c_str (), quote (expected).c_str ()); |
| } |
| } |
| |
| /* Entry point for cp_remove_params unit tests. */ |
| |
| static void |
| test_cp_remove_params () |
| { |
| /* Check that removing parameter info out of NAME produces EXPECTED. |
| Checks both normal and completion modes. */ |
| #define CHECK(NAME, EXPECTED) \ |
| do \ |
| { \ |
| check_remove_params (__FILE__, __LINE__, NAME, EXPECTED, false); \ |
| check_remove_params (__FILE__, __LINE__, NAME, EXPECTED, true); \ |
| } \ |
| while (0) |
| |
| /* Similar, but used when NAME is incomplete -- i.e., is has |
| unbalanced parentheses. In this case, looking for the exact name |
| should fail / return empty. */ |
| #define CHECK_INCOMPL(NAME, EXPECTED) \ |
| do \ |
| { \ |
| check_remove_params (__FILE__, __LINE__, NAME, NULL, false); \ |
| check_remove_params (__FILE__, __LINE__, NAME, EXPECTED, true); \ |
| } \ |
| while (0) |
| |
| CHECK ("function()", "function"); |
| CHECK_INCOMPL ("function(", "function"); |
| CHECK ("function() const", "function"); |
| |
| CHECK ("(anonymous namespace)::A::B::C", |
| "(anonymous namespace)::A::B::C"); |
| |
| CHECK ("A::(anonymous namespace)", |
| "A::(anonymous namespace)"); |
| |
| CHECK_INCOMPL ("A::(anonymou", "A"); |
| |
| CHECK ("A::foo<int>()", |
| "A::foo<int>"); |
| |
| CHECK_INCOMPL ("A::foo<int>(", |
| "A::foo<int>"); |
| |
| CHECK ("A::foo<(anonymous namespace)::B>::func(int)", |
| "A::foo<(anonymous namespace)::B>::func"); |
| |
| CHECK_INCOMPL ("A::foo<(anonymous namespace)::B>::func(in", |
| "A::foo<(anonymous namespace)::B>::func"); |
| |
| CHECK_INCOMPL ("A::foo<(anonymous namespace)::B>::", |
| "A::foo<(anonymous namespace)::B>"); |
| |
| CHECK_INCOMPL ("A::foo<(anonymous namespace)::B>:", |
| "A::foo<(anonymous namespace)::B>"); |
| |
| CHECK ("A::foo<(anonymous namespace)::B>", |
| "A::foo<(anonymous namespace)::B>"); |
| |
| CHECK_INCOMPL ("A::foo<(anonymous namespace)::B", |
| "A::foo"); |
| |
| /* Shouldn't this parse? Looks like a bug in |
| cp_demangled_name_to_comp. See PR c++/22411. */ |
| #if 0 |
| CHECK ("A::foo<void(int)>::func(int)", |
| "A::foo<void(int)>::func"); |
| #else |
| CHECK_INCOMPL ("A::foo<void(int)>::func(int)", |
| "A::foo"); |
| #endif |
| |
| CHECK_INCOMPL ("A::foo<void(int", |
| "A::foo"); |
| |
| #undef CHECK |
| #undef CHECK_INCOMPL |
| } |
| |
| } // namespace selftests |
| |
| #endif /* GDB_SELF_CHECK */ |
| |
| /* This is a front end for cp_find_first_component, for unit testing. |
| Be careful when using it: see the NOTE above |
| cp_find_first_component. */ |
| |
| static void |
| first_component_command (const char *arg, int from_tty) |
| { |
| int len; |
| char *prefix; |
| |
| if (!arg) |
| return; |
| |
| len = cp_find_first_component (arg); |
| prefix = (char *) alloca (len + 1); |
| |
| memcpy (prefix, arg, len); |
| prefix[len] = '\0'; |
| |
| printf_filtered ("%s\n", prefix); |
| } |
| |
| /* Implement "info vtbl". */ |
| |
| static void |
| info_vtbl_command (const char *arg, int from_tty) |
| { |
| struct value *value; |
| |
| value = parse_and_eval (arg); |
| cplus_print_vtable (value); |
| } |
| |
| /* See description in cp-support.h. */ |
| |
| const char * |
| find_toplevel_char (const char *s, char c) |
| { |
| int quoted = 0; /* zero if we're not in quotes; |
| '"' if we're in a double-quoted string; |
| '\'' if we're in a single-quoted string. */ |
| int depth = 0; /* Number of unclosed parens we've seen. */ |
| const char *scan; |
| |
| for (scan = s; *scan; scan++) |
| { |
| if (quoted) |
| { |
| if (*scan == quoted) |
| quoted = 0; |
| else if (*scan == '\\' && *(scan + 1)) |
| scan++; |
| } |
| else if (*scan == c && ! quoted && depth == 0) |
| return scan; |
| else if (*scan == '"' || *scan == '\'') |
| quoted = *scan; |
| else if (*scan == '(' || *scan == '<') |
| depth++; |
| else if ((*scan == ')' || *scan == '>') && depth > 0) |
| depth--; |
| else if (*scan == 'o' && !quoted && depth == 0) |
| { |
| /* Handle C++ operator names. */ |
| if (strncmp (scan, CP_OPERATOR_STR, CP_OPERATOR_LEN) == 0) |
| { |
| scan += CP_OPERATOR_LEN; |
| if (*scan == c) |
| return scan; |
| while (ISSPACE (*scan)) |
| { |
| ++scan; |
| if (*scan == c) |
| return scan; |
| } |
| if (*scan == '\0') |
| break; |
| |
| switch (*scan) |
| { |
| /* Skip over one less than the appropriate number of |
| characters: the for loop will skip over the last |
| one. */ |
| case '<': |
| if (scan[1] == '<') |
| { |
| scan++; |
| if (*scan == c) |
| return scan; |
| } |
| break; |
| case '>': |
| if (scan[1] == '>') |
| { |
| scan++; |
| if (*scan == c) |
| return scan; |
| } |
| break; |
| } |
| } |
| } |
| } |
| |
| return 0; |
| } |
| |
| void _initialize_cp_support (); |
| void |
| _initialize_cp_support () |
| { |
| cmd_list_element *maintenance_cplus |
| = add_basic_prefix_cmd ("cplus", class_maintenance, |
| _("C++ maintenance commands."), |
| &maint_cplus_cmd_list, |
| 0, &maintenancelist); |
| add_alias_cmd ("cp", maintenance_cplus, class_maintenance, 1, |
| &maintenancelist); |
| |
| add_cmd ("first_component", |
| class_maintenance, |
| first_component_command, |
| _("Print the first class/namespace component of NAME."), |
| &maint_cplus_cmd_list); |
| |
| add_info ("vtbl", info_vtbl_command, |
| _("Show the virtual function table for a C++ object.\n\ |
| Usage: info vtbl EXPRESSION\n\ |
| Evaluate EXPRESSION and display the virtual function table for the\n\ |
| resulting object.")); |
| |
| #ifdef HAVE_WORKING_FORK |
| add_setshow_boolean_cmd ("catch-demangler-crashes", class_maintenance, |
| &catch_demangler_crashes, _("\ |
| Set whether to attempt to catch demangler crashes."), _("\ |
| Show whether to attempt to catch demangler crashes."), _("\ |
| If enabled GDB will attempt to catch demangler crashes and\n\ |
| display the offending symbol."), |
| NULL, |
| NULL, |
| &maintenance_set_cmdlist, |
| &maintenance_show_cmdlist); |
| |
| gdb_demangle_attempt_core_dump = can_dump_core (LIMIT_CUR); |
| #endif |
| |
| #if GDB_SELF_TEST |
| selftests::register_test ("cp_symbol_name_matches", |
| selftests::test_cp_symbol_name_matches); |
| selftests::register_test ("cp_remove_params", |
| selftests::test_cp_remove_params); |
| #endif |
| } |