diff --git a/gdb/cp-name-parser.y b/gdb/cp-name-parser.y index 8736777..f7060fa 100644 --- a/gdb/cp-name-parser.y +++ b/gdb/cp-name-parser.y @@ -60,7 +60,7 @@ static const char *lexptr, *prev_lexptr, *error_lexptr, *global_errmsg; struct demangle_info { int used; - struct demangle_info *prev, *next; + struct demangle_info *next; struct demangle_component comps[ALLOC_CHUNK]; }; @@ -76,7 +76,6 @@ d_grab (void) if (demangle_info->next == NULL) { more = malloc (sizeof (struct demangle_info)); - more->prev = demangle_info; more->next = NULL; demangle_info->next = more; } @@ -1935,20 +1934,14 @@ yyerror (char *msg) generally allocate too many components, but the extra memory usage doesn't hurt because the trees are temporary and the storage is reused. More may be allocated later, by d_grab. */ -static void +static struct demangle_info * allocate_info (void) { - if (demangle_info == NULL) - { - demangle_info = malloc (sizeof (struct demangle_info)); - demangle_info->prev = NULL; - demangle_info->next = NULL; - } - else - while (demangle_info->prev) - demangle_info = demangle_info->prev; + struct demangle_info *info = malloc (sizeof (struct demangle_info)); - demangle_info->used = 0; + info->next = NULL; + info->used = 0; + return info; } /* Convert RESULT to a string. The return value is allocated @@ -1966,23 +1959,86 @@ cp_comp_to_string (struct demangle_component *result, int estimated_len) &err); } +/* Free any memory associated with the given PARSE_INFO. */ + +void +cp_demangled_name_parse_free (struct demangle_parse_info *parse_info) +{ + struct demangle_info *info = parse_info->info; + + /* Free any allocated chunks of memory for the parse. */ + while (info != NULL) + { + struct demangle_info *next = info->next; + + free (info); + info = next; + } + + /* Free the parser info. */ + free (parse_info); +} + +/* Merge the two parse trees given by DEST and SRC. The parse tree + in SRC is attached to DEST at the node represented by TARGET. + SRC is then freed. */ + +void +cp_merge_demangle_parse_infos (struct demangle_parse_info *dest, + struct demangle_component *target, + struct demangle_parse_info *src) + +{ + struct demangle_info *di; + + memcpy (target, src->tree, sizeof (struct demangle_component)); + di = dest->info; + while (di->next != NULL) + di = di->next; + di->next = src->info; + src->info = NULL; + cp_demangled_name_parse_free (src); +} + +/* A cleanup wrapper for cp_demangled_name_parse_free. */ + +static void +do_demangled_name_parse_free_cleanup (void *data) +{ + struct demangle_parse_info *info = (struct demangle_parse_info *) data; + + cp_demangled_name_parse_free (info); +} + +/* Create a cleanup for C++ name parsing. */ + +struct cleanup * +make_cleanup_cp_demangled_name_parse_free (struct demangle_parse_info *info) +{ + return make_cleanup (do_demangled_name_parse_free_cleanup, info); +} + /* Convert a demangled name to a demangle_component tree. On success, - the root of the new tree is returned; it is valid until the next - call to this function and should not be freed. On error, NULL is + a structure containing the root of the new tree is returned; it must + be freed by calling cp_demangled_name_parse_free. On error, NULL is returned, and an error message will be set in *ERRMSG (which does not need to be freed). */ -struct demangle_component * +struct demangle_parse_info * cp_demangled_name_to_comp (const char *demangled_name, const char **errmsg) { static char errbuf[60]; - struct demangle_component *result; + struct demangle_parse_info *result; prev_lexptr = lexptr = demangled_name; error_lexptr = NULL; global_errmsg = NULL; - allocate_info (); + demangle_info = allocate_info (); + + result = ((struct demangle_parse_info *) + malloc (sizeof (struct demangle_parse_info))); + result->info = demangle_info; if (yyparse ()) { @@ -1993,10 +2049,11 @@ cp_demangled_name_to_comp (const char *demangled_name, const char **errmsg) strcat (errbuf, "'"); *errmsg = errbuf; } + cp_demangled_name_parse_free (result); return NULL; } - result = global_result; + result->tree = global_result; global_result = NULL; return result; @@ -2052,7 +2109,7 @@ main (int argc, char **argv) char buf[65536]; int arg; const char *errmsg; - struct demangle_component *result; + struct demangle_parse_info *result; arg = 1; if (argv[arg] && strcmp (argv[arg], "--debug") == 0) @@ -2086,7 +2143,8 @@ main (int argc, char **argv) continue; } - cp_print (result); + cp_print (result->tree); + cp_demangled_name_parse_free (result); free (str2); if (c) @@ -2105,7 +2163,8 @@ main (int argc, char **argv) fputc ('\n', stderr); return 0; } - cp_print (result); + cp_print (result->tree); + cp_demangled_name_parse_free (result); putchar ('\n'); } return 0; diff --git a/gdb/cp-support.c b/gdb/cp-support.c index a479067..924fff4 100644 --- a/gdb/cp-support.c +++ b/gdb/cp-support.c @@ -86,6 +86,22 @@ static const char *operator_tokens[] = /* new[] and delete[] require special whitespace handling */ }; +/* 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" + }; + +/* A vector used to define a free list for typedef replacement + (replace_typedefs/inspect_type). See cp_canonicalize_string_no_typdefs + and inspect_type for more information. */ +DEF_VEC_P (char_ptr); + +static void + replace_typedefs (struct demangle_parse_info *info, + struct demangle_component *ret_comp, + VEC (char_ptr) **free_list); + /* 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 @@ -117,6 +133,365 @@ cp_already_canonical (const char *string) return 0; } +/* Inspect the given RET_COMP for its type. If it is a typedef, + replace the node with the typedef's tree, storing any memory allocations + on the FREE_LIST. + + Returns 1 if any typedef substitutions were made, 0 otherwise. */ + +static int +inspect_type (struct demangle_parse_info *info, + struct demangle_component *ret_comp, + VEC (char_ptr) **free_list) +{ + int i; + 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 (i = 0; i < ARRAY_SIZE (ignore_typedefs); ++i) + { + if (strcmp (name, ignore_typedefs[i]) == 0) + return 0; + } + + sym = lookup_symbol (name, 0, VAR_DOMAIN, 0); + if (sym != NULL) + { + struct type *otype = SYMBOL_TYPE (sym); + + /* If the type is a typedef, replace it. */ + if (TYPE_CODE (otype) == TYPE_CODE_TYPEDEF) + { + long len; + int is_anon; + struct type *type; + struct demangle_parse_info *i; + struct ui_file *buf = mem_fileopen (); + struct cleanup *cleanup = make_cleanup_ui_file_delete (buf); + + /* Get the real type of the typedef. */ + type = check_typedef (otype); + + is_anon = (TYPE_TAG_NAME (type) == NULL + && (TYPE_CODE (type) == TYPE_CODE_ENUM + || TYPE_CODE (type) == TYPE_CODE_STRUCT + || TYPE_CODE (type) == TYPE_CODE_UNION)); + if (is_anon) + { + struct type *last = otype; + + /* Find the last typedef for the type. */ + while (TYPE_TARGET_TYPE (last) != NULL + && (TYPE_CODE (TYPE_TARGET_TYPE (last)) + == 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) + { + do_cleanups (cleanup); + return 0; + } + else + /* Use the last typedef seen as the type for this + anonymous type. */ + type = last; + } + + + type_print (type, "", buf, -1); + name = ui_file_xstrdup (buf, &len); + VEC_safe_push (char_ptr, *free_list, name); + do_cleanups (cleanup); + + /* 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); + + /* 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, free_list); + } + 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). */ + char *canon = cp_canonicalize_string_no_typedefs (name); + + if (canon != NULL) + { + xfree (name); + name = canon; + } + + ret_comp->u.s_name.s = name; + ret_comp->u.s_name.len = len; + } + + return 1; + } + } + + return 0; +} + +/* Replace any typedefs appearing in the qualified name + (DEMANGLE_COMPONENT_QUAL_NAME) represented in RET_COMP for the name parse + given in INFO. Store any allocated memory in FREE_LIST. */ + +static void +replace_typedefs_qualified_name (struct demangle_parse_info *info, + struct demangle_component *ret_comp, + VEC (char_ptr) **free_list) +{ + long len; + char *name; + struct ui_file *buf = mem_fileopen (); + 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_NAME) + { + struct demangle_component new; + + ui_file_write (buf, d_left (comp)->u.s_name.s, + d_left (comp)->u.s_name.len); + name = ui_file_xstrdup (buf, &len); + new.type = DEMANGLE_COMPONENT_NAME; + new.u.s_name.s = name; + new.u.s_name.len = len; + if (inspect_type (info, &new, free_list)) + { + char *n; + + /* A typedef was substituted in NEW. Convert it to a + string and replace the top DEMANGLE_COMPONENT_QUAL_NAME + node. */ + n = cp_comp_to_string (&new, 100); + xfree (name); + if (n != NULL) + { + ui_file_rewind (buf); + VEC_safe_push (char_ptr, *free_list, n); + + d_left (ret_comp)->u.s_name.s = n; + d_left (ret_comp)->u.s_name.len = strlen (n); + 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), free_list); + name = cp_comp_to_string (d_left (comp), 100); + if (name != NULL) + { + fputs_unfiltered (name, buf); + xfree (name); + } + } + ui_file_write (buf, "::", 2); + comp = d_right (comp); + } + + /* If the next component is 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_NAME) + { + ui_file_write (buf, comp->u.s_name.s, comp->u.s_name.len); + name = ui_file_xstrdup (buf, &len); + VEC_safe_push (char_ptr, *free_list, name); + + /* 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 = name; + ret_comp->u.s_name.len = len; + inspect_type (info, ret_comp, free_list); + } + else + replace_typedefs (info, comp, free_list); + + ui_file_delete (buf); +} + + +/* 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. Any required memory allocations are added + to the FREE_LIST, which must be free'd by a caller. */ + +static void +replace_typedefs (struct demangle_parse_info *info, + struct demangle_component *ret_comp, + VEC (char_ptr) **free_list) +{ + if (ret_comp) + { + 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: + replace_typedefs (info, d_left (ret_comp), free_list); + replace_typedefs (info, d_right (ret_comp), free_list); + break; + + case DEMANGLE_COMPONENT_TYPED_NAME: + { + struct demangle_component *comp = d_right (ret_comp); + + while (comp != NULL + && (comp->type == DEMANGLE_COMPONENT_VOLATILE + || comp->type == DEMANGLE_COMPONENT_RESTRICT + || comp->type == DEMANGLE_COMPONENT_CONST + || comp->type == DEMANGLE_COMPONENT_VOLATILE_THIS + || comp->type == DEMANGLE_COMPONENT_RESTRICT_THIS + || comp->type == DEMANGLE_COMPONENT_CONST_THIS)) + comp = d_left (comp); + + if (d_left (ret_comp)->type != DEMANGLE_COMPONENT_NAME + || comp->type != DEMANGLE_COMPONENT_FUNCTION_TYPE) + replace_typedefs (info, d_left (ret_comp), free_list); + replace_typedefs (info, d_right (ret_comp), free_list); + } + break; + + case DEMANGLE_COMPONENT_NAME: + inspect_type (info, ret_comp, free_list); + break; + + case DEMANGLE_COMPONENT_QUAL_NAME: + replace_typedefs_qualified_name (info, ret_comp, free_list); + 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), free_list); + 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: + replace_typedefs (info, d_left (ret_comp), free_list); + break; + + default: + break; + } + } +} + +/* Parse STRING and convert it to canonical form, resolving any typedefs. + If parsing fails, or if STRING is already canonical, return NULL. + Otherwise return the canonical form. The return value is allocated via + xmalloc. */ + +char * +cp_canonicalize_string_no_typedefs (const char *string) +{ + int i; + char *ret, *iter; + unsigned int estimated_len; + struct demangle_parse_info *info; + VEC (char_ptr) *free_list; + + ret = NULL; + free_list = VEC_alloc (char_ptr, 10); + 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, info->tree, &free_list); + + /* Convert the tree back into a string. */ + ret = cp_comp_to_string (info->tree, estimated_len); + gdb_assert (ret != NULL); + + /* Free the parse information. */ + cp_demangled_name_parse_free (info); + + /* Free any memory allocated during typedef replacement. */ + for (i = 0; VEC_iterate (char_ptr, free_list, i, iter); ++i) + xfree (iter); + + /* Free the vector used for the free list. */ + VEC_free (char_ptr, free_list); + + /* Finally, compare the original string with the computed + name, returning NULL if they are the same. */ + if (strcmp (string, ret) == 0) + { + xfree (ret); + return NULL; + } + } + + return ret; +} + /* Parse STRING and convert it to canonical form. If parsing fails, or if STRING is already canonical, return NULL. Otherwise return the canonical form. The return value is allocated via xmalloc. */ @@ -124,19 +499,20 @@ cp_already_canonical (const char *string) char * cp_canonicalize_string (const char *string) { - struct demangle_component *ret_comp; + struct demangle_parse_info *info; unsigned int estimated_len; char *ret; if (cp_already_canonical (string)) return NULL; - ret_comp = cp_demangled_name_to_comp (string, NULL); - if (ret_comp == NULL) + info = cp_demangled_name_to_comp (string, NULL); + if (info == NULL) return NULL; estimated_len = strlen (string) * 2; - ret = cp_comp_to_string (ret_comp, estimated_len); + ret = cp_comp_to_string (info->tree, estimated_len); + cp_demangled_name_parse_free (info); if (strcmp (string, ret) == 0) { @@ -153,23 +529,29 @@ cp_canonicalize_string (const char *string) freed when finished with the tree, or NULL if none was needed. OPTIONS will be passed to the demangler. */ -static struct demangle_component * +static struct demangle_parse_info * mangled_name_to_comp (const char *mangled_name, int options, void **memory, char **demangled_p) { - struct demangle_component *ret; char *demangled_name; + struct demangle_parse_info *info; /* 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) { + info = ((struct demangle_parse_info *) + xmalloc (sizeof (struct demangle_parse_info))); + info->info = NULL; + info->tree = ret; *demangled_p = NULL; - return ret; + return info; } } @@ -181,16 +563,16 @@ mangled_name_to_comp (const char *mangled_name, int options, /* If we could demangle the name, parse it to build the component tree. */ - ret = cp_demangled_name_to_comp (demangled_name, NULL); + info = cp_demangled_name_to_comp (demangled_name, NULL); - if (ret == NULL) + if (info == NULL) { xfree (demangled_name); return NULL; } *demangled_p = demangled_name; - return ret; + return info; } /* Return the name of the class containing method PHYSNAME. */ @@ -201,14 +583,16 @@ cp_class_name_from_physname (const char *physname) void *storage = NULL; char *demangled_name = NULL, *ret; struct demangle_component *ret_comp, *prev_comp, *cur_comp; + struct demangle_parse_info *info; int done; - ret_comp = mangled_name_to_comp (physname, DMGL_ANSI, - &storage, &demangled_name); - if (ret_comp == NULL) + 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. */ @@ -277,8 +661,8 @@ cp_class_name_from_physname (const char *physname) } xfree (storage); - if (demangled_name) - xfree (demangled_name); + xfree (demangled_name); + cp_demangled_name_parse_free (info); return ret; } @@ -348,13 +732,14 @@ method_name_from_physname (const char *physname) void *storage = NULL; char *demangled_name = NULL, *ret; struct demangle_component *ret_comp; + struct demangle_parse_info *info; - ret_comp = mangled_name_to_comp (physname, DMGL_ANSI, - &storage, &demangled_name); - if (ret_comp == NULL) + info = mangled_name_to_comp (physname, DMGL_ANSI, + &storage, &demangled_name); + if (info == NULL) return NULL; - ret_comp = unqualified_name_from_comp (ret_comp); + ret_comp = unqualified_name_from_comp (info->tree); ret = NULL; if (ret_comp != NULL) @@ -363,8 +748,8 @@ method_name_from_physname (const char *physname) ret = cp_comp_to_string (ret_comp, 10); xfree (storage); - if (demangled_name) - xfree (demangled_name); + xfree (demangled_name); + cp_demangled_name_parse_free (info); return ret; } @@ -379,17 +764,19 @@ cp_func_name (const char *full_name) { char *ret; struct demangle_component *ret_comp; + struct demangle_parse_info *info; - ret_comp = cp_demangled_name_to_comp (full_name, NULL); - if (!ret_comp) + info = cp_demangled_name_to_comp (full_name, NULL); + if (!info) return NULL; - ret_comp = unqualified_name_from_comp (ret_comp); + ret_comp = unqualified_name_from_comp (info->tree); ret = NULL; if (ret_comp != NULL) ret = cp_comp_to_string (ret_comp, 10); + cp_demangled_name_parse_free (info); return ret; } @@ -402,16 +789,18 @@ cp_remove_params (const char *demangled_name) { int done = 0; struct demangle_component *ret_comp; + struct demangle_parse_info *info; char *ret = NULL; if (demangled_name == NULL) return NULL; - ret_comp = cp_demangled_name_to_comp (demangled_name, NULL); - if (ret_comp == 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) { @@ -433,6 +822,7 @@ cp_remove_params (const char *demangled_name) if (ret_comp->type == DEMANGLE_COMPONENT_TYPED_NAME) ret = cp_comp_to_string (d_left (ret_comp), 10); + cp_demangled_name_parse_free (info); return ret; } diff --git a/gdb/cp-support.h b/gdb/cp-support.h index d23f19e..65e0c69 100644 --- a/gdb/cp-support.h +++ b/gdb/cp-support.h @@ -46,6 +46,17 @@ struct demangle_component; #define CP_ANONYMOUS_NAMESPACE_LEN 21 +/* The result of parsing a name. */ + +struct demangle_parse_info +{ + /* The memory used during the parse. */ + struct demangle_info *info; + + /* The result of the parse. */ + struct demangle_component *tree; +}; + /* This struct is designed to store data from using directives. It says that names from namespace IMPORT_SRC should be visible within namespace IMPORT_DEST. These form a linked list; NEXT is the next @@ -134,6 +145,8 @@ struct using_direct extern char *cp_canonicalize_string (const char *string); +extern char *cp_canonicalize_string_no_typedefs (const char *string); + extern char *cp_class_name_from_physname (const char *physname); extern char *method_name_from_physname (const char *physname); @@ -214,12 +227,19 @@ struct type *cp_lookup_transparent_type (const char *name); /* Functions from cp-name-parser.y. */ -extern struct demangle_component *cp_demangled_name_to_comp - (const char *demangled_name, const char **errmsg); +extern struct demangle_parse_info *cp_demangled_name_to_comp + (const char *demangled_name, const char **errmsg); extern char *cp_comp_to_string (struct demangle_component *result, int estimated_len); +extern void cp_demangled_name_parse_free (struct demangle_parse_info *); +extern struct cleanup *make_cleanup_cp_demangled_name_parse_free + (struct demangle_parse_info *); +extern void cp_merge_demangle_parse_infos (struct demangle_parse_info *, + struct demangle_component *, + struct demangle_parse_info *); + /* The list of "maint cplus" commands. */ extern struct cmd_list_element *maint_cplus_cmd_list; diff --git a/gdb/linespec.c b/gdb/linespec.c index 137ef9c..b96c79f 100644 --- a/gdb/linespec.c +++ b/gdb/linespec.c @@ -245,7 +245,7 @@ find_methods (struct type *t, char *name, enum language language, /* NAME is typed by the user: it needs to be canonicalized before passing to lookup_symbol. */ - canon = cp_canonicalize_string (name); + canon = cp_canonicalize_string_no_typedefs (name); if (canon != NULL) { name = canon; @@ -1365,7 +1365,7 @@ decode_compound (char **argptr, int funfirstline, char *the_real_saved_arg, char *p) { struct symtabs_and_lines values; - char *p2; + char *p2, *name, *canon; char *saved_arg2 = *argptr; char *temp_end; struct symbol *sym; @@ -1373,6 +1373,7 @@ decode_compound (char **argptr, int funfirstline, struct symbol *sym_class; struct type *t; char *saved_arg; + struct cleanup *cleanup; /* If the user specified any completer quote characters in the input, strip them. They are superfluous. */ @@ -1597,7 +1598,18 @@ decode_compound (char **argptr, int funfirstline, *argptr = (*p == '\'') ? p + 1 : p; /* Look up entire name. */ - sym = lookup_symbol (copy, get_selected_block (0), VAR_DOMAIN, 0); + name = copy; + + cleanup = make_cleanup (null_cleanup, NULL); + canon = cp_canonicalize_string_no_typedefs (copy); + if (canon != NULL) + { + name = canon; + make_cleanup (xfree, name); + } + + sym = lookup_symbol (name, get_selected_block (0), VAR_DOMAIN, 0); + do_cleanups (cleanup); if (sym) return symbol_found (funfirstline, canonical, copy, sym, NULL, NULL); else @@ -1743,7 +1755,7 @@ find_method (int funfirstline, struct linespec_result *canonical, strcpy (name, SYMBOL_NATURAL_NAME (sym_class)); strcat (name, "::"); strcat (name, copy); - canon = cp_canonicalize_string (name); + canon = cp_canonicalize_string_no_typedefs (name); if (canon != NULL) { xfree (name); @@ -2085,16 +2097,31 @@ decode_variable (char *copy, int funfirstline, struct linespec_result *canonical, struct symtab *file_symtab) { + char *name, *canon; struct symbol *sym; + struct cleanup *cleanup; struct minimal_symbol *msymbol; - sym = lookup_symbol (copy, get_search_block (file_symtab), - VAR_DOMAIN, 0); + name = copy; + cleanup = make_cleanup (null_cleanup, NULL); + canon = cp_canonicalize_string_no_typedefs (copy); + if (canon != NULL) + { + name = canon; + make_cleanup (xfree, name); + } + + sym = lookup_symbol (name, get_search_block (file_symtab), VAR_DOMAIN, 0); if (sym != NULL) - return symbol_found (funfirstline, canonical, copy, sym, file_symtab, NULL); + { + do_cleanups (cleanup); + return symbol_found (funfirstline, canonical, copy, sym, + file_symtab, NULL); + } - msymbol = lookup_minimal_symbol (copy, NULL, NULL); + msymbol = lookup_minimal_symbol (name, NULL, NULL); + do_cleanups (cleanup); if (msymbol != NULL) return minsym_found (funfirstline, msymbol); diff --git a/gdb/python/py-type.c b/gdb/python/py-type.c index 8ab18cf..335342e 100644 --- a/gdb/python/py-type.c +++ b/gdb/python/py-type.c @@ -577,8 +577,10 @@ typy_legacy_template_argument (struct type *type, struct block *block, { int i; struct demangle_component *demangled; + struct demangle_parse_info *info; const char *err; struct type *argtype; + struct cleanup *cleanup; if (TYPE_NAME (type) == NULL) { @@ -587,12 +589,14 @@ typy_legacy_template_argument (struct type *type, struct block *block, } /* Note -- this is not thread-safe. */ - demangled = cp_demangled_name_to_comp (TYPE_NAME (type), &err); - if (! demangled) + info = cp_demangled_name_to_comp (TYPE_NAME (type), &err); + if (! info) { PyErr_SetString (PyExc_RuntimeError, err); return NULL; } + demangled = info->tree; + cleanup = make_cleanup_cp_demangled_name_parse_free (info); /* Strip off component names. */ while (demangled->type == DEMANGLE_COMPONENT_QUAL_NAME @@ -601,6 +605,7 @@ typy_legacy_template_argument (struct type *type, struct block *block, if (demangled->type != DEMANGLE_COMPONENT_TEMPLATE) { + do_cleanups (cleanup); PyErr_SetString (PyExc_RuntimeError, _("Type is not a template.")); return NULL; } @@ -613,12 +618,14 @@ typy_legacy_template_argument (struct type *type, struct block *block, if (! demangled) { + do_cleanups (cleanup); PyErr_Format (PyExc_RuntimeError, _("No argument %d in template."), argno); return NULL; } argtype = typy_lookup_type (demangled->u.s_binary.left, block); + do_cleanups (cleanup); if (! argtype) return NULL; diff --git a/gdb/testsuite/gdb.cp/meth-typedefs.cc b/gdb/testsuite/gdb.cp/meth-typedefs.cc new file mode 100644 index 0000000..b3c68cc --- /dev/null +++ b/gdb/testsuite/gdb.cp/meth-typedefs.cc @@ -0,0 +1,149 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2011 Free Software Foundation, Inc. + + 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 . + + Contributed by Red Hat, originally written by Keith Seitz. */ + +#include + +typedef const char* const* my_type; +typedef int my_type_2; +typedef my_type my_other_type; +typedef my_type_2 my_other_type_2; +typedef unsigned long CORE_ADDR; +typedef enum {E_A, E_B, E_C} anon_enum; +typedef struct {int a; char b;} anon_struct; +typedef union {int a; char b;} anon_union; +typedef anon_enum aenum; +typedef anon_struct astruct; +typedef anon_union aunion; + +typedef void (*fptr1) (my_other_type); +typedef void (*fptr2) (fptr1, my_other_type_2); +typedef void (*fptr3) (fptr2, my_other_type); +typedef void (*fptr4) (anon_enum a, anon_struct const& b, anon_union const*** c); + +namespace A +{ + class foo + { + public: + foo (void) { } + foo (my_other_type a) { } // A::foo::foo(my_other_type) + foo (my_other_type_2 a) { } // A::foo::foo(my_other_type_2) + foo (my_other_type_2 a, const my_other_type b) { } // A::foo::foo(my_other_type_2, const my_other_type) + foo (fptr3) { } // A::foo::foo(fptr3) + foo (fptr1* a) { } // A::foo::foo(fptr1*) + foo (CORE_ADDR (*) [10]) { } // A::foo::foo(CORE_ADDR (*) [10]) + foo (aenum a, astruct const& b, aunion const*** c) { } // A::foo::foo(aenum, astruct const&, aunion const***) + + void test (my_other_type a) { } // A::foo::test(my_other_type) + void test (my_other_type_2 a) { } // A::foo::test(my_other_type_2) + void test (my_other_type_2 a, const my_other_type b) { } // A::foo::test(my_other_type_2, const my_other_type) + void test (fptr3 a) { } // A::foo::test(fptr3) + void test (fptr1* a) { } // A::foo::test(fptr1*) + void test (CORE_ADDR (*) [10]) { } // A::foo::test(CORE_ADDR (*) [10]) + void test (aenum a, astruct const& b, aunion const*** c) { }; // A::foo::test(aenum, astruct const&, aunion const***) + }; +}; + +namespace B +{ + void + test (my_other_type foo) { } // B::test(my_other_type) + + void + test (aenum a, astruct const& b, aunion const*** c) { } // B::test(aenum, astruct const&, aunion const***) + + template + void test (T1 a, T2 b) { } // B::test (T1, T2) + + template <> + void test (my_other_type foo, my_other_type_2) { } // B::test(my_other_type, my_other_type_2) +}; + +namespace a +{ + namespace b + { + namespace c + { + namespace d + { + class bar { }; + } + } + + typedef c::d::bar BAR; + } +} + +typedef a::b::BAR _BAR_; + +template +void test (T1 a, T2 b) {} // test (T1, T2) + +template <> +void test (my_other_type foo, my_other_type_2) { } // test(my_other_type, my_other_type_2) + +void +test (my_other_type foo) { } // test(my_other_type) + +void +test (_BAR_ &b) { } // test(_BAR_&) + +void +test (aenum a, astruct const& b, aunion const*** c) { } // test(aenum, astruct const&, aunion const***) + +int +main (void) +{ + A::foo my_foo; + fptr1 fptr; + astruct as = { 0, 0 }; + aunion const au = { 0 }; + aunion const* aup = &au; + aunion const** aupp = &aup; + aunion const*** auppp = &aupp; + + my_foo.test (static_cast (NULL)); + my_foo.test (0); + my_foo.test (0, static_cast (NULL)); + my_foo.test (static_cast (NULL)); + my_foo.test (&fptr); + my_foo.test (static_cast (0)); + my_foo.test (E_A, as, auppp); + + B::test (static_cast (NULL)); + B::test (static_cast (NULL), 0); + B::test (E_A, as, auppp); + + test (static_cast (NULL)); + test (static_cast (NULL), 0); + test (E_A, as, auppp); + + A::foo a (static_cast (NULL)); + A::foo b (0); + A::foo c (0, static_cast (NULL)); + A::foo d (static_cast (NULL)); + A::foo e (&fptr); + A::foo f (static_cast (0)); + A::foo g (E_A, as, auppp); + + fptr4 f4; + + return 0; +} diff --git a/gdb/testsuite/gdb.cp/meth-typedefs.exp b/gdb/testsuite/gdb.cp/meth-typedefs.exp new file mode 100644 index 0000000..851ec02 --- /dev/null +++ b/gdb/testsuite/gdb.cp/meth-typedefs.exp @@ -0,0 +1,160 @@ +# Copyright 2011 Free Software Foundation, Inc. +# +# Contributed by Red Hat, originally written by Keith Seitz. +# +# 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 . + +# This file is part of the gdb testsuite. + +proc add {var name params expected {kind {func}}} { + upvar $var result + + if {[string compare $kind "template"] == 0} { + set method_name "${name}<$expected>" + } else { + set method_name "$name" + } + + set expect ".*// ${method_name}\\($expected\\)" + lappend result [list "${method_name}($params)" $expect] +} + +if {[skip_cplus_tests]} { continue } + +# Tests for c++/12266 et al +set testfile "meth-typedefs" +set srcfile $testfile.cc + +if {[prepare_for_testing $testfile $testfile $srcfile {c++ debug}]} { + return -1 +} + +if {![runto_main]} { + perror "couldn't run to breakpoint" + continue +} + +array set typedefs { + "my_other_type" {"my_other_type" "my_type" "const char* const*"} + "my_other_type_2" {"my_other_type_2" "my_type_2" "int"} + "CORE_ADDR" { "CORE_ADDR" "unsigned long" } + "_BAR_" { "_BAR_" "a::b::BAR" "a::b::c::d::bar" } + "aenum" { "aenum" "anon_enum" } + "astruct" { "astruct" "anon_struct" } + "aunion" { "aunion" "anon_union" } +} + +set methods {} + +# Add the simple, one-parameter methods +foreach meth {A::foo::test A::foo::foo} { + foreach type {my_other_type my_other_type_2} { + foreach t $typedefs($type) { + add methods $meth $t $type + } + } +} + +# Add two-parameter methods +foreach meth {A::foo::test A::foo::foo} { + set type "my_other_type_2, const my_other_type" + foreach t1 $typedefs(my_other_type_2) { + foreach t2 $typedefs(my_other_type) { + add methods $meth "$t1, const $t2" $type + add methods $meth "$t1, $t2" $type + } + } +} + +# Add three-parameter methods/functions +foreach meth {A::foo::test A::foo::foo B::test test} { + set type "aenum, astruct const&, aunion const\\*\\*\\*" + foreach t1 $typedefs(aenum) { + foreach t2 $typedefs(astruct) { + foreach t3 $typedefs(aunion) { + add methods $meth "$t1, $t2 const&, $t3 const***" $type + } + } + } +} + +# Add the array-of-function pointer methods +set type "fptr1\\*" +foreach meth {A::foo::test A::foo::foo} { + add methods $meth "fptr1*" $type + foreach t $typedefs(my_other_type) { + add methods $meth "void (**) ($t)" $type + } +} + +# Add the function pointer methods +set type "fptr3" +foreach meth {A::foo::test A::foo::foo} { + add methods $meth "fptr3" $type + + foreach t1 $typedefs(my_other_type) { + add methods $meth "void (*)(fptr2, $t1)" $type + foreach t2 $typedefs(my_other_type_2) { + add methods $meth "void (*)(void (*)(fptr1, $t2), $t1)" $type + foreach t3 $typedefs(my_other_type) { + add methods $meth \ + "void (*)(void (*)(void (*) ($t3), $t2), $t1)" $type + } + } + } +} + +set type1 "my_other_type" +set type2 "my_other_type, my_other_type_2" +foreach meth {"test" "B::test"} { + foreach t1 $typedefs(my_other_type) { + add methods $meth $t1 $type1 + foreach t2 $typedefs(my_other_type_2) { + add methods $meth "$t1, $t2" $type2 template + } + } +} + +# Miscellaneous tests +set type {CORE_ADDR \(\*\) \[10\]} +foreach meth {A::foo::foo A::foo::test} { + foreach t $typedefs(CORE_ADDR) { + add methods $meth "$t (*) \[10\]" $type + } +} + +foreach t $typedefs(_BAR_) { + add methods "test" "$t&" {_BAR_&} +} + +gdb_test_no_output "set listsize 1" "" + +# Finally, for each method in the list METHODS, check whether +# the user can "list" it and "break" on it (both quoted and unquoted). +foreach test $methods { + set func [lindex $test 0] + set result [lindex $test 1] + + gdb_test "list $func" $result + gdb_test "list '$func'" $result + if {[gdb_breakpoint $func]} { + pass "break $func" + } + if {[gdb_breakpoint '$func']} { + pass "break '$func'" + } +} + +gdb_exit +return 0