Index: gdb/linespec.c |
diff --git a/gdb/linespec.c b/gdb/linespec.c |
index 54e699fc1243220c1fabf8a754ba4d64f878ffe3..683299968a52c8a112b5cac634a50170b377f02c 100644 |
--- a/gdb/linespec.c |
+++ b/gdb/linespec.c |
@@ -43,6 +43,7 @@ |
#include "cli/cli-utils.h" |
#include "filenames.h" |
#include "ada-lang.h" |
+#include "stack.h" |
typedef struct symtab *symtab_p; |
DEF_VEC_P (symtab_p); |
@@ -63,12 +64,108 @@ struct address_entry |
CORE_ADDR addr; |
}; |
+/* A helper struct which just holds a minimal symbol and the object |
+ file from which it came. */ |
+ |
+typedef struct minsym_and_objfile |
+{ |
+ struct minimal_symbol *minsym; |
+ struct objfile *objfile; |
+} minsym_and_objfile_d; |
+ |
+DEF_VEC_O (minsym_and_objfile_d); |
+ |
+/* An enumeration of possible signs for a line offset. */ |
+enum offset_relative_sign |
+{ |
+ /* No sign */ |
+ LINE_OFFSET_NONE, |
+ |
+ /* A plus sign ("+") */ |
+ LINE_OFFSET_PLUS, |
+ |
+ /* A minus sign ("-") */ |
+ LINE_OFFSET_MINUS, |
+ |
+ /* A special "sign" for unspecified offset. */ |
+ LINE_OFFSET_UNKNOWN |
+}; |
+ |
+/* A line offset in a linespec. */ |
+ |
+struct line_offset |
+{ |
+ /* Line offset and any specified sign. */ |
+ int offset; |
+ enum offset_relative_sign sign; |
+}; |
+ |
+/* A linespec. Elements of this structure are filled in by a parser |
+ (either parse_linespec or some other function). The structure is |
+ then converted into SALs by convert_linespec_to_sals. */ |
+ |
+struct linespec |
+{ |
+ /* An expression and the resulting PC. Specifying an expression |
+ currently precludes the use of other members. */ |
+ |
+ /* The expression entered by the user. */ |
+ char *expression; |
+ |
+ /* The resulting PC expression derived from evaluating EXPRESSION. */ |
+ CORE_ADDR expr_pc; |
+ |
+ /* Any specified file symtabs. */ |
+ |
+ /* The user-supplied source filename or NULL if none was specified. */ |
+ char *source_filename; |
+ |
+ /* The list of symtabs to search to which to limit the search. May not |
+ be NULL. If SOURCE_FILENAME is NULL (no user-specified filename), |
+ FILE_SYMTABS should contain one single NULL member. This will |
+ cause the code to use the default symtab. */ |
+ VEC (symtab_p) *file_symtabs; |
+ |
+ /* The name of a function or method and any matching symbols. */ |
+ |
+ /* The user-specified function name. If no function name was |
+ supplied, this may be NULL. */ |
+ char *function_name; |
+ |
+ /* A list of matching function symbols and minimal symbols. Both lists |
+ may be NULL if no matching symbols were found. */ |
+ VEC (symbolp) *function_symbols; |
+ VEC (minsym_and_objfile_d) *minimal_symbols; |
+ |
+ /* The name of a label and matching symbols. */ |
+ |
+ /* The user-specified label name. */ |
+ char *label_name; |
+ |
+ /* A structure of matching label symbols and the corresponding |
+ function symbol in which the label was found. Both may be NULL |
+ or both must be non-NULL. */ |
+ struct |
+ { |
+ VEC (symbolp) *label_symbols; |
+ VEC (symbolp) *function_symbols; |
+ } labels; |
+ |
+ /* Line offset. It may be LINE_OFFSET_UNKNOWN, meaning that no |
+ offset was specified. */ |
+ struct line_offset line_offset; |
+}; |
+typedef struct linespec *linespec_p; |
+ |
/* An instance of this is used to keep all state while linespec |
operates. This instance is passed around as a 'this' pointer to |
the various implementation methods. */ |
struct linespec_state |
{ |
+ /* The language in use during linespec processing. */ |
+ const struct language_defn *language; |
+ |
/* The program space as seen when the module was entered. */ |
struct program_space *program_space; |
@@ -78,19 +175,6 @@ struct linespec_state |
/* The default line to use. */ |
int default_line; |
- /* If the linespec started with "FILE:", this holds all the matching |
- symtabs. Otherwise, it will hold a single NULL entry, meaning |
- that the default symtab should be used. */ |
- VEC (symtab_p) *file_symtabs; |
- |
- /* If the linespec started with "FILE:", this holds an xmalloc'd |
- copy of "FILE". */ |
- char *user_filename; |
- |
- /* If the linespec is "FUNCTION:LABEL", this holds an xmalloc'd copy |
- of "FUNCTION". */ |
- char *user_function; |
- |
/* The 'funfirstline' value that was passed in to decode_line_1 or |
decode_line_full. */ |
int funfirstline; |
@@ -117,67 +201,133 @@ struct collect_info |
/* The linespec object in use. */ |
struct linespec_state *state; |
+ /* A list of symtabs to which to restrict matches. */ |
+ VEC (symtab_p) *file_symtabs; |
+ |
/* The result being accumulated. */ |
- struct symtabs_and_lines result; |
+ struct |
+ { |
+ VEC (symbolp) *symbols; |
+ VEC (minsym_and_objfile_d) *minimal_symbols; |
+ } result; |
}; |
-/* Prototypes for local functions. */ |
+/* Token types */ |
-static void initialize_defaults (struct symtab **default_symtab, |
- int *default_line); |
+enum ls_token_type |
+{ |
+ /* A keyword */ |
+ LSTOKEN_KEYWORD = 0, |
-static struct symtabs_and_lines decode_indirect (struct linespec_state *self, |
- char **argptr); |
+ /* A colon "separator" */ |
+ LSTOKEN_COLON, |
-static char *locate_first_half (char **argptr, int *is_quote_enclosed); |
+ /* A string */ |
+ LSTOKEN_STRING, |
-static struct symtabs_and_lines decode_objc (struct linespec_state *self, |
- char **argptr); |
+ /* A number */ |
+ LSTOKEN_NUMBER, |
+ |
+ /* A comma */ |
+ LSTOKEN_COMMA, |
+ |
+ /* EOI (end of input) */ |
+ LSTOKEN_EOI, |
+ |
+ /* Consumed token */ |
+ LSTOKEN_CONSUMED |
+}; |
+typedef enum ls_token_type linespec_token_type; |
+ |
+/* List of keywords */ |
+ |
+static const char * const linespec_keywords[] = { "if", "thread", "task" }; |
+ |
+/* A token of the linespec lexer */ |
+ |
+struct ls_token |
+{ |
+ /* The type of the token */ |
+ linespec_token_type type; |
+ |
+ /* Data for the token */ |
+ union |
+ { |
+ /* A string, given as a stoken */ |
+ struct stoken string; |
+ |
+ /* A keyword */ |
+ const char *keyword; |
+ } data; |
+}; |
+typedef struct ls_token linespec_token; |
+ |
+#define LS_TOKEN_STOKEN(TOK) (TOK).data.string |
+#define LS_TOKEN_KEYWORD(TOK) (TOK).data.keyword |
+ |
+/* An instance of the linespec parser. */ |
+ |
+struct ls_parser |
+{ |
+ /* Lexer internal data */ |
+ struct |
+ { |
+ /* Save head of input stream. */ |
+ char *saved_arg; |
-static struct symtabs_and_lines decode_compound (struct linespec_state *self, |
- char **argptr, |
- char *saved_arg, |
- char *p); |
+ /* Head of the input stream. */ |
+ char **stream; |
+#define PARSER_STREAM(P) (*(P)->lexer.stream) |
-static VEC (symbolp) *lookup_prefix_sym (char **argptr, char *p, |
- VEC (symtab_p) *, |
- char **); |
+ /* The current token. */ |
+ linespec_token current; |
+ } lexer; |
-static struct symtabs_and_lines find_method (struct linespec_state *self, |
- char *saved_arg, |
- char *copy, |
- const char *class_name, |
- VEC (symbolp) *sym_classes); |
+ /* Is the entire linespec quote-enclosed? */ |
+ int is_quote_enclosed; |
+ |
+ /* Is a keyword syntactically valid at this point? |
+ In, e.g., "break thread thread 1", the leading "keyword" must not |
+ be interpreted as such. */ |
+ int keyword_ok; |
+ |
+ /* The state of the parse. */ |
+ struct linespec_state state; |
+#define PARSER_STATE(PPTR) (&(PPTR)->state) |
-static void cplusplus_error (const char *name, const char *fmt, ...) |
- ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (2, 3); |
+ /* The result of the parse. */ |
+ struct linespec result; |
+#define PARSER_RESULT(PPTR) (&(PPTR)->result) |
+}; |
+typedef struct ls_parser linespec_parser; |
-static char *find_toplevel_char (char *s, char c); |
+/* Prototypes for local functions. */ |
-static int is_objc_method_format (const char *s); |
+static void initialize_defaults (struct symtab **default_symtab, |
+ int *default_line); |
-static VEC (symtab_p) *symtabs_from_filename (char **argptr, |
- char *p, int is_quote_enclosed, |
- char **user_filename); |
+static CORE_ADDR linespec_expression_to_pc (char **exp_ptr); |
-static VEC (symbolp) *find_function_symbols (char **argptr, char *p, |
- int is_quote_enclosed, |
- char **user_function); |
+static struct symtabs_and_lines decode_objc (struct linespec_state *self, |
+ linespec_p ls, |
+ char **argptr); |
-static struct symtabs_and_lines decode_all_digits (struct linespec_state *self, |
- char **argptr, |
- char *q); |
+static VEC (symtab_p) *symtabs_from_filename (const char *); |
-static struct symtabs_and_lines decode_dollar (struct linespec_state *self, |
- char *copy); |
+static VEC (symbolp) *find_label_symbols (struct linespec_state *self, |
+ VEC (symbolp) *function_symbols, |
+ VEC (symbolp) **label_funcs_ret, |
+ const char *name); |
-static int decode_label (struct linespec_state *self, |
- VEC (symbolp) *function_symbols, |
- char *copy, |
- struct symtabs_and_lines *result); |
+void find_linespec_symbols (struct linespec_state *self, |
+ VEC (symtab_p) *file_symtabs, |
+ const char *name, |
+ VEC (symbolp) **symbols, |
+ VEC (minsym_and_objfile_d) **minsyms); |
-static struct symtabs_and_lines decode_variable (struct linespec_state *self, |
- char *copy); |
+static struct line_offset |
+ linespec_parse_variable (struct linespec_state *self, |
+ const char *variable); |
static int symbol_to_sal (struct symtab_and_line *result, |
int funfirstline, struct symbol *sym); |
@@ -190,238 +340,740 @@ static void add_all_symbol_names_from_pspace (struct collect_info *info, |
struct program_space *pspace, |
VEC (const_char_ptr) *names); |
-/* Helper functions. */ |
+static VEC (symtab_p) *collect_symtabs_from_filename (const char *file); |
-/* Add SAL to SALS. */ |
+static void decode_digits_ordinary (struct linespec_state *self, |
+ linespec_p ls, |
+ int line, |
+ struct symtabs_and_lines *sals, |
+ struct linetable_entry **best_entry); |
-static void |
-add_sal_to_sals_basic (struct symtabs_and_lines *sals, |
- struct symtab_and_line *sal) |
-{ |
- ++sals->nelts; |
- sals->sals = xrealloc (sals->sals, sals->nelts * sizeof (sals->sals[0])); |
- sals->sals[sals->nelts - 1] = *sal; |
-} |
+static void decode_digits_list_mode (struct linespec_state *self, |
+ linespec_p ls, |
+ struct symtabs_and_lines *values, |
+ struct symtab_and_line val); |
-/* Add SAL to SALS, and also update SELF->CANONICAL_NAMES to reflect |
- the new sal, if needed. If not NULL, SYMNAME is the name of the |
- symbol to use when constructing the new canonical name. */ |
+static void minsym_found (struct linespec_state *self, struct objfile *objfile, |
+ struct minimal_symbol *msymbol, |
+ struct symtabs_and_lines *result); |
-static void |
-add_sal_to_sals (struct linespec_state *self, |
- struct symtabs_and_lines *sals, |
- struct symtab_and_line *sal, |
- const char *symname) |
+static int compare_symbols (const void *a, const void *b); |
+ |
+static int compare_msymbols (const void *a, const void *b); |
+ |
+static const char *find_toplevel_char (const char *s, char c); |
+ |
+/* Permitted quote characters for the parser. This is different from the |
+ completer's quote characters to allow backward compatibility with the |
+ previous parser. */ |
+static const char *const linespec_quote_characters = "\"\'"; |
+ |
+/* Lexer functions. */ |
+ |
+/* Lex a number from the input in PARSER. This only supports |
+ decimal numbers. |
+ |
+ Return true if input is decimal numbers. Return false if not. */ |
+ |
+static int |
+linespec_lexer_lex_number (linespec_parser *parser, linespec_token *tokenp) |
{ |
- add_sal_to_sals_basic (sals, sal); |
+ tokenp->type = LSTOKEN_NUMBER; |
+ LS_TOKEN_STOKEN (*tokenp).length = 0; |
+ LS_TOKEN_STOKEN (*tokenp).ptr = PARSER_STREAM (parser); |
- if (self->canonical) |
+ /* Keep any sign at the start of the stream. */ |
+ if (*PARSER_STREAM (parser) == '+' || *PARSER_STREAM (parser) == '-') |
{ |
- char *canonical_name = NULL; |
- |
- self->canonical_names = xrealloc (self->canonical_names, |
- sals->nelts * sizeof (char *)); |
- if (sal->symtab && sal->symtab->filename) |
- { |
- char *filename = sal->symtab->filename; |
+ ++LS_TOKEN_STOKEN (*tokenp).length; |
+ ++(PARSER_STREAM (parser)); |
+ } |
- /* Note that the filter doesn't have to be a valid linespec |
- input. We only apply the ":LINE" treatment to Ada for |
- the time being. */ |
- if (symname != NULL && sal->line != 0 |
- && current_language->la_language == language_ada) |
- canonical_name = xstrprintf ("%s:%s:%d", filename, symname, |
- sal->line); |
- else if (symname != NULL) |
- canonical_name = xstrprintf ("%s:%s", filename, symname); |
- else |
- canonical_name = xstrprintf ("%s:%d", filename, sal->line); |
- } |
+ while (isdigit (*PARSER_STREAM (parser))) |
+ { |
+ ++LS_TOKEN_STOKEN (*tokenp).length; |
+ ++(PARSER_STREAM (parser)); |
+ } |
- self->canonical_names[sals->nelts - 1] = canonical_name; |
+ /* If the next character in the input buffer is not a space, comma, |
+ quote, or colon, the input does not represent a number. */ |
+ if (*PARSER_STREAM (parser) != '\0' |
+ && !isspace (*PARSER_STREAM (parser)) && *PARSER_STREAM (parser) != ',' |
+ && *PARSER_STREAM (parser) != ':' |
+ && !strchr (linespec_quote_characters, *PARSER_STREAM (parser))) |
+ { |
+ PARSER_STREAM (parser) = LS_TOKEN_STOKEN (*tokenp).ptr; |
+ return 0; |
} |
+ |
+ return 1; |
} |
-/* A hash function for address_entry. */ |
+/* Does P represent one of the keywords? If so, return |
+ the keyword. If not, return NULL. */ |
-static hashval_t |
-hash_address_entry (const void *p) |
+static const char * |
+linespec_lexer_lex_keyword (const char *p) |
{ |
- const struct address_entry *aep = p; |
- hashval_t hash; |
+ int i; |
- hash = iterative_hash_object (aep->pspace, 0); |
- return iterative_hash_object (aep->addr, hash); |
+ if (p != NULL) |
+ { |
+ for (i = 0; i < ARRAY_SIZE (linespec_keywords); ++i) |
+ { |
+ int len = strlen (linespec_keywords[i]); |
+ |
+ /* If P begins with one of the keywords and the next |
+ character is not a valid identifier character, |
+ we have found a keyword. */ |
+ if (strncmp (p, linespec_keywords[i], len) == 0 |
+ && !(isalnum (p[len]) || p[len] == '_')) |
+ return linespec_keywords[i]; |
+ } |
+ } |
+ |
+ return NULL; |
} |
-/* An equality function for address_entry. */ |
+/* Does STRING represent an Ada operator? If so, return the length |
+ of the decoded operator name. If not, return 0. */ |
static int |
-eq_address_entry (const void *a, const void *b) |
+is_ada_operator (const char *string) |
{ |
- const struct address_entry *aea = a; |
- const struct address_entry *aeb = b; |
+ const struct ada_opname_map *mapping; |
- return aea->pspace == aeb->pspace && aea->addr == aeb->addr; |
+ for (mapping = ada_opname_table; |
+ mapping->encoded != NULL |
+ && strncmp (mapping->decoded, string, |
+ strlen (mapping->decoded)) != 0; ++mapping) |
+ ; |
+ |
+ return mapping->decoded == NULL ? 0 : strlen (mapping->decoded); |
} |
-/* Check whether the address, represented by PSPACE and ADDR, is |
- already in the set. If so, return 0. Otherwise, add it and return |
- 1. */ |
+/* Find QUOTE_CHAR in STRING, accounting for the ':' terminal. Return |
+ the location of QUOTE_CHAR, or NULL if not found. */ |
-static int |
-maybe_add_address (htab_t set, struct program_space *pspace, CORE_ADDR addr) |
+static const char * |
+skip_quote_char (const char *string, char quote_char) |
{ |
- struct address_entry e, *p; |
- void **slot; |
- |
- e.pspace = pspace; |
- e.addr = addr; |
- slot = htab_find_slot (set, &e, INSERT); |
- if (*slot) |
- return 0; |
+ const char *p, *last; |
- p = XNEW (struct address_entry); |
- memcpy (p, &e, sizeof (struct address_entry)); |
- *slot = p; |
+ p = last = find_toplevel_char (string, quote_char); |
+ while (p && *p != '\0' && *p != ':') |
+ { |
+ p = find_toplevel_char (p, quote_char); |
+ if (p != NULL) |
+ last = p++; |
+ } |
- return 1; |
+ return last; |
} |
-/* Issue a helpful hint on using the command completion feature on |
- single quoted demangled C++ symbols as part of the completion |
- error. */ |
+/* Make a writable copy of the string given in TOKEN, trimming |
+ any trailing whitespace. */ |
-static void |
-cplusplus_error (const char *name, const char *fmt, ...) |
+static char * |
+copy_token_string (linespec_token token) |
{ |
- struct ui_file *tmp_stream; |
- char *message; |
+ char *str, *s; |
- tmp_stream = mem_fileopen (); |
- make_cleanup_ui_file_delete (tmp_stream); |
+ if (token.type == LSTOKEN_KEYWORD) |
+ return xstrdup (LS_TOKEN_KEYWORD (token)); |
- { |
- va_list args; |
- |
- va_start (args, fmt); |
- vfprintf_unfiltered (tmp_stream, fmt, args); |
- va_end (args); |
- } |
- |
- while (*name == '\'') |
- name++; |
- fprintf_unfiltered (tmp_stream, |
- ("Hint: try '%s<TAB> or '%s<ESC-?>\n" |
- "(Note leading single quote.)"), |
- name, name); |
+ str = savestring (LS_TOKEN_STOKEN (token).ptr, |
+ LS_TOKEN_STOKEN (token).length); |
+ s = remove_trailing_whitespace (str, str + LS_TOKEN_STOKEN (token).length); |
+ *s = '\0'; |
- message = ui_file_xstrdup (tmp_stream, NULL); |
- make_cleanup (xfree, message); |
- throw_error (NOT_FOUND_ERROR, "%s", message); |
+ return str; |
} |
-/* A helper for iterate_over_all_matching_symtabs that is passed as a |
- callback to the expand_symtabs_matching method. */ |
+/* Does P represent the end of a quote-enclosed linespec? */ |
static int |
-iterate_name_matcher (const struct language_defn *language, |
- const char *name, void *d) |
+is_closing_quote_enclosed (const char *p) |
{ |
- const char **dname = d; |
- |
- if (language->la_symbol_name_compare (name, *dname) == 0) |
- return 1; |
- return 0; |
+ if (strchr (linespec_quote_characters, *p)) |
+ ++p; |
+ p = skip_spaces ((char *) p); |
+ return (*p == '\0' || linespec_lexer_lex_keyword (p)); |
} |
-/* A helper that walks over all matching symtabs in all objfiles and |
- calls CALLBACK for each symbol matching NAME. If SEARCH_PSPACE is |
- not NULL, then the search is restricted to just that program |
- space. */ |
+/* Find the end of the parameter list that starts with *INPUT. |
+ This helper function assists with lexing string segments |
+ which might contain valid (non-terminating) commas. */ |
-static void |
-iterate_over_all_matching_symtabs (const char *name, |
- const domain_enum domain, |
- int (*callback) (struct symbol *, void *), |
- void *data, |
- struct program_space *search_pspace) |
+static char * |
+find_parameter_list_end (char *input) |
{ |
- struct objfile *objfile; |
- struct program_space *pspace; |
- |
- ALL_PSPACES (pspace) |
- { |
- if (search_pspace != NULL && search_pspace != pspace) |
- continue; |
- if (pspace->executing_startup) |
- continue; |
+ char end_char, start_char; |
+ int depth; |
+ char *p; |
- set_current_program_space (pspace); |
+ start_char = *input; |
+ if (start_char == '(') |
+ end_char = ')'; |
+ else if (start_char == '<') |
+ end_char = '>'; |
+ else |
+ return NULL; |
- ALL_OBJFILES (objfile) |
+ p = input; |
+ depth = 0; |
+ while (*p) |
{ |
- struct symtab *symtab; |
- |
- if (objfile->sf) |
- objfile->sf->qf->expand_symtabs_matching (objfile, NULL, |
- iterate_name_matcher, |
- ALL_DOMAIN, |
- &name); |
- |
- ALL_OBJFILE_SYMTABS (objfile, symtab) |
+ if (*p == start_char) |
+ ++depth; |
+ else if (*p == end_char) |
{ |
- if (symtab->primary) |
+ if (--depth == 0) |
{ |
- struct block *block; |
- |
- block = BLOCKVECTOR_BLOCK (BLOCKVECTOR (symtab), STATIC_BLOCK); |
- LA_ITERATE_OVER_SYMBOLS (block, name, domain, callback, data); |
+ ++p; |
+ break; |
} |
} |
+ ++p; |
} |
- } |
+ |
+ return p; |
} |
-/* Returns the block to be used for symbol searches for the given SYMTAB, |
- which may be NULL. */ |
-static struct block * |
-get_search_block (struct symtab *symtab) |
+/* Lex a string from the input in PARSER. */ |
+ |
+static linespec_token |
+linespec_lexer_lex_string (linespec_parser *parser) |
{ |
- struct block *block; |
+ linespec_token token; |
+ char *start = PARSER_STREAM (parser); |
- if (symtab != NULL) |
- block = BLOCKVECTOR_BLOCK (BLOCKVECTOR (symtab), STATIC_BLOCK); |
- else |
+ token.type = LSTOKEN_STRING; |
+ |
+ /* If the input stream starts with a quote character, skip to the next |
+ quote character, regardless of the content. */ |
+ if (strchr (linespec_quote_characters, *PARSER_STREAM (parser))) |
{ |
- enum language save_language; |
+ const char *end; |
+ char quote_char = *PARSER_STREAM (parser); |
- /* get_selected_block can change the current language when there is |
- no selected frame yet. */ |
- save_language = current_language->la_language; |
- block = get_selected_block (0); |
- set_language (save_language); |
- } |
+ /* Special case: Ada operators. */ |
+ if (PARSER_STATE (parser)->language->la_language == language_ada |
+ && quote_char == '\"') |
+ { |
+ int len = is_ada_operator (PARSER_STREAM (parser)); |
- return block; |
-} |
+ if (len != 0) |
+ { |
+ /* The input is an Ada operator. Return the quoted string |
+ as-is. */ |
+ LS_TOKEN_STOKEN (token).ptr = PARSER_STREAM (parser); |
+ LS_TOKEN_STOKEN (token).length = len; |
+ PARSER_STREAM (parser) += len; |
+ return token; |
+ } |
-/* A helper for find_method. This finds all methods in type T which |
- match NAME. It adds resulting symbol names to RESULT_NAMES, and |
- adds T's direct superclasses to SUPERCLASSES. */ |
+ /* The input does not represent an Ada operator -- fall through |
+ to normal quoted string handling. */ |
+ } |
-static void |
-find_methods (struct type *t, const char *name, |
- VEC (const_char_ptr) **result_names, |
- VEC (typep) **superclasses) |
-{ |
- int i1 = 0; |
- int ibase; |
- char *class_name = type_name_no_tag (t); |
- char *canon; |
+ /* Skip past the beginning quote. */ |
+ ++(PARSER_STREAM (parser)); |
- /* Ignore this class if it doesn't have a name. This is ugly, but |
+ /* Mark the start of the string. */ |
+ LS_TOKEN_STOKEN (token).ptr = PARSER_STREAM (parser); |
+ |
+ /* Skip to the ending quote. */ |
+ end = skip_quote_char (PARSER_STREAM (parser), quote_char); |
+ |
+ /* Error if the input did not terminate properly. */ |
+ if (end == NULL) |
+ error (_("unmatched quote")); |
+ |
+ /* Skip over the ending quote and mark the length of the string. */ |
+ PARSER_STREAM (parser) = (char *) ++end; |
+ LS_TOKEN_STOKEN (token).length = PARSER_STREAM (parser) - 2 - start; |
+ } |
+ else |
+ { |
+ char *p; |
+ |
+ /* Otherwise, only identifier characters are permitted. |
+ Spaces are the exception. In general, we keep spaces, |
+ but only if the next characters in the input do not resolve |
+ to one of the keywords. |
+ |
+ This allows users to forgo quoting CV-qualifiers, template arguments, |
+ and similar common language constructs. */ |
+ |
+ while (1) |
+ { |
+ if (isspace (*PARSER_STREAM (parser))) |
+ { |
+ p = skip_spaces (PARSER_STREAM (parser)); |
+ /* When we get here we know we've found something followed by |
+ a space (we skip over parens and templates below). |
+ So if we find a keyword now, we know it is a keyword and not, |
+ say, a function name. */ |
+ if (linespec_lexer_lex_keyword (p) != NULL) |
+ { |
+ LS_TOKEN_STOKEN (token).ptr = start; |
+ LS_TOKEN_STOKEN (token).length |
+ = PARSER_STREAM (parser) - start; |
+ return token; |
+ } |
+ |
+ /* Advance past the whitespace. */ |
+ PARSER_STREAM (parser) = p; |
+ } |
+ |
+ /* If the next character is EOI or (single) ':', the |
+ string is complete; return the token. */ |
+ if (*PARSER_STREAM (parser) == 0) |
+ { |
+ LS_TOKEN_STOKEN (token).ptr = start; |
+ LS_TOKEN_STOKEN (token).length = PARSER_STREAM (parser) - start; |
+ return token; |
+ } |
+ else if (PARSER_STREAM (parser)[0] == ':') |
+ { |
+ /* Do not tokenize the C++ scope operator. */ |
+ if (PARSER_STREAM (parser)[1] == ':') |
+ ++(PARSER_STREAM (parser)); |
+ |
+ /* Do not tokenify if the input length so far is one |
+ (i.e, a single-letter drive name) and the next character |
+ is a directory separator. This allows Windows-style |
+ paths to be recognized as filenames without quoting it. */ |
+ else if ((PARSER_STREAM (parser) - start) != 1 |
+ || !IS_DIR_SEPARATOR (PARSER_STREAM (parser)[1])) |
+ { |
+ LS_TOKEN_STOKEN (token).ptr = start; |
+ LS_TOKEN_STOKEN (token).length |
+ = PARSER_STREAM (parser) - start; |
+ return token; |
+ } |
+ } |
+ /* Special case: permit quote-enclosed linespecs. */ |
+ else if (parser->is_quote_enclosed |
+ && strchr (linespec_quote_characters, |
+ *PARSER_STREAM (parser)) |
+ && is_closing_quote_enclosed (PARSER_STREAM (parser))) |
+ { |
+ LS_TOKEN_STOKEN (token).ptr = start; |
+ LS_TOKEN_STOKEN (token).length = PARSER_STREAM (parser) - start; |
+ return token; |
+ } |
+ /* Because commas may terminate a linespec and appear in |
+ the middle of valid string input, special cases for |
+ '<' and '(' are necessary. */ |
+ else if (*PARSER_STREAM (parser) == '<' |
+ || *PARSER_STREAM (parser) == '(') |
+ { |
+ char *p; |
+ |
+ p = find_parameter_list_end (PARSER_STREAM (parser)); |
+ if (p != NULL) |
+ { |
+ PARSER_STREAM (parser) = p; |
+ continue; |
+ } |
+ } |
+ /* Commas are terminators, but not if they are part of an |
+ operator name. */ |
+ else if (*PARSER_STREAM (parser) == ',') |
+ { |
+ if ((PARSER_STATE (parser)->language->la_language |
+ == language_cplus) |
+ && (PARSER_STREAM (parser) - start) > 8 |
+ /* strlen ("operator") */) |
+ { |
+ char *p = strstr (start, "operator"); |
+ |
+ if (p != NULL && is_operator_name (p)) |
+ { |
+ /* This is an operator name. Keep going. */ |
+ ++(PARSER_STREAM (parser)); |
+ continue; |
+ } |
+ } |
+ |
+ /* Comma terminates the string. */ |
+ LS_TOKEN_STOKEN (token).ptr = start; |
+ LS_TOKEN_STOKEN (token).length = PARSER_STREAM (parser) - start; |
+ return token; |
+ } |
+ |
+ /* Advance the stream. */ |
+ ++(PARSER_STREAM (parser)); |
+ } |
+ } |
+ |
+ return token; |
+} |
+ |
+/* Lex a single linespec token from PARSER. */ |
+ |
+static linespec_token |
+linespec_lexer_lex_one (linespec_parser *parser) |
+{ |
+ const char *keyword; |
+ |
+ if (parser->lexer.current.type == LSTOKEN_CONSUMED) |
+ { |
+ /* Skip any whitespace. */ |
+ PARSER_STREAM (parser) = skip_spaces (PARSER_STREAM (parser)); |
+ |
+ /* Check for a keyword, they end the linespec. */ |
+ keyword = NULL; |
+ if (parser->keyword_ok) |
+ keyword = linespec_lexer_lex_keyword (PARSER_STREAM (parser)); |
+ if (keyword != NULL) |
+ { |
+ parser->lexer.current.type = LSTOKEN_KEYWORD; |
+ LS_TOKEN_KEYWORD (parser->lexer.current) = keyword; |
+ return parser->lexer.current; |
+ } |
+ |
+ /* Handle other tokens. */ |
+ switch (*PARSER_STREAM (parser)) |
+ { |
+ case 0: |
+ parser->lexer.current.type = LSTOKEN_EOI; |
+ break; |
+ |
+ case '+': case '-': |
+ case '0': case '1': case '2': case '3': case '4': |
+ case '5': case '6': case '7': case '8': case '9': |
+ if (!linespec_lexer_lex_number (parser, &(parser->lexer.current))) |
+ parser->lexer.current = linespec_lexer_lex_string (parser); |
+ break; |
+ |
+ case ':': |
+ /* If we have a scope operator, lex the input as a string. |
+ Otherwise, return LSTOKEN_COLON. */ |
+ if (PARSER_STREAM (parser)[1] == ':') |
+ parser->lexer.current = linespec_lexer_lex_string (parser); |
+ else |
+ { |
+ parser->lexer.current.type = LSTOKEN_COLON; |
+ ++(PARSER_STREAM (parser)); |
+ } |
+ break; |
+ |
+ case '\'': case '\"': |
+ /* Special case: permit quote-enclosed linespecs. */ |
+ if (parser->is_quote_enclosed |
+ && is_closing_quote_enclosed (PARSER_STREAM (parser))) |
+ { |
+ ++(PARSER_STREAM (parser)); |
+ parser->lexer.current.type = LSTOKEN_EOI; |
+ } |
+ else |
+ parser->lexer.current = linespec_lexer_lex_string (parser); |
+ break; |
+ |
+ case ',': |
+ parser->lexer.current.type = LSTOKEN_COMMA; |
+ LS_TOKEN_STOKEN (parser->lexer.current).ptr |
+ = PARSER_STREAM (parser); |
+ LS_TOKEN_STOKEN (parser->lexer.current).length = 1; |
+ ++(PARSER_STREAM (parser)); |
+ break; |
+ |
+ default: |
+ /* If the input is not a number, it must be a string. |
+ [Keywords were already considered above.] */ |
+ parser->lexer.current = linespec_lexer_lex_string (parser); |
+ break; |
+ } |
+ } |
+ |
+ return parser->lexer.current; |
+} |
+ |
+/* Consume the current token and return the next token in PARSER's |
+ input stream. */ |
+ |
+static linespec_token |
+linespec_lexer_consume_token (linespec_parser *parser) |
+{ |
+ parser->lexer.current.type = LSTOKEN_CONSUMED; |
+ return linespec_lexer_lex_one (parser); |
+} |
+ |
+/* Return the next token without consuming the current token. */ |
+ |
+static linespec_token |
+linespec_lexer_peek_token (linespec_parser *parser) |
+{ |
+ linespec_token next; |
+ char *saved_stream = PARSER_STREAM (parser); |
+ linespec_token saved_token = parser->lexer.current; |
+ |
+ next = linespec_lexer_consume_token (parser); |
+ PARSER_STREAM (parser) = saved_stream; |
+ parser->lexer.current = saved_token; |
+ return next; |
+} |
+ |
+/* Helper functions. */ |
+ |
+/* Add SAL to SALS. */ |
+ |
+static void |
+add_sal_to_sals_basic (struct symtabs_and_lines *sals, |
+ struct symtab_and_line *sal) |
+{ |
+ ++sals->nelts; |
+ sals->sals = xrealloc (sals->sals, sals->nelts * sizeof (sals->sals[0])); |
+ sals->sals[sals->nelts - 1] = *sal; |
+} |
+ |
+/* Add SAL to SALS, and also update SELF->CANONICAL_NAMES to reflect |
+ the new sal, if needed. If not NULL, SYMNAME is the name of the |
+ symbol to use when constructing the new canonical name. */ |
+ |
+static void |
+add_sal_to_sals (struct linespec_state *self, |
+ struct symtabs_and_lines *sals, |
+ struct symtab_and_line *sal, |
+ const char *symname) |
+{ |
+ add_sal_to_sals_basic (sals, sal); |
+ |
+ if (self->canonical) |
+ { |
+ char *canonical_name = NULL; |
+ |
+ self->canonical_names = xrealloc (self->canonical_names, |
+ sals->nelts * sizeof (char *)); |
+ if (sal->symtab && sal->symtab->filename) |
+ { |
+ char *filename = sal->symtab->filename; |
+ |
+ /* Note that the filter doesn't have to be a valid linespec |
+ input. We only apply the ":LINE" treatment to Ada for |
+ the time being. */ |
+ if (symname != NULL && sal->line != 0 |
+ && self->language->la_language == language_ada) |
+ canonical_name = xstrprintf ("%s:%s:%d", filename, symname, |
+ sal->line); |
+ else if (symname != NULL) |
+ canonical_name = xstrprintf ("%s:%s", filename, symname); |
+ else |
+ canonical_name = xstrprintf ("%s:%d", filename, sal->line); |
+ } |
+ |
+ self->canonical_names[sals->nelts - 1] = canonical_name; |
+ } |
+} |
+ |
+/* A hash function for address_entry. */ |
+ |
+static hashval_t |
+hash_address_entry (const void *p) |
+{ |
+ const struct address_entry *aep = p; |
+ hashval_t hash; |
+ |
+ hash = iterative_hash_object (aep->pspace, 0); |
+ return iterative_hash_object (aep->addr, hash); |
+} |
+ |
+/* An equality function for address_entry. */ |
+ |
+static int |
+eq_address_entry (const void *a, const void *b) |
+{ |
+ const struct address_entry *aea = a; |
+ const struct address_entry *aeb = b; |
+ |
+ return aea->pspace == aeb->pspace && aea->addr == aeb->addr; |
+} |
+ |
+/* Check whether the address, represented by PSPACE and ADDR, is |
+ already in the set. If so, return 0. Otherwise, add it and return |
+ 1. */ |
+ |
+static int |
+maybe_add_address (htab_t set, struct program_space *pspace, CORE_ADDR addr) |
+{ |
+ struct address_entry e, *p; |
+ void **slot; |
+ |
+ e.pspace = pspace; |
+ e.addr = addr; |
+ slot = htab_find_slot (set, &e, INSERT); |
+ if (*slot) |
+ return 0; |
+ |
+ p = XNEW (struct address_entry); |
+ memcpy (p, &e, sizeof (struct address_entry)); |
+ *slot = p; |
+ |
+ return 1; |
+} |
+ |
+/* A callback function and the additional data to call it with. */ |
+ |
+struct symbol_and_data_callback |
+{ |
+ /* The callback to use. */ |
+ symbol_found_callback_ftype *callback; |
+ |
+ /* Data to be passed to the callback. */ |
+ void *data; |
+}; |
+ |
+/* A helper for iterate_over_all_matching_symtabs that is used to |
+ restrict calls to another callback to symbols representing inline |
+ symbols only. */ |
+ |
+static int |
+iterate_inline_only (struct symbol *sym, void *d) |
+{ |
+ if (SYMBOL_INLINED (sym)) |
+ { |
+ struct symbol_and_data_callback *cad = d; |
+ |
+ return cad->callback (sym, cad->data); |
+ } |
+ return 1; /* Continue iterating. */ |
+} |
+ |
+/* Some data for the expand_symtabs_matching callback. */ |
+ |
+struct symbol_matcher_data |
+{ |
+ /* The lookup name against which symbol name should be compared. */ |
+ const char *lookup_name; |
+ |
+ /* The routine to be used for comparison. */ |
+ symbol_name_cmp_ftype symbol_name_cmp; |
+}; |
+ |
+/* A helper for iterate_over_all_matching_symtabs that is passed as a |
+ callback to the expand_symtabs_matching method. */ |
+ |
+static int |
+iterate_name_matcher (const char *name, void *d) |
+{ |
+ const struct symbol_matcher_data *data = d; |
+ |
+ if (data->symbol_name_cmp (name, data->lookup_name) == 0) |
+ return 1; /* Expand this symbol's symbol table. */ |
+ return 0; /* Skip this symbol. */ |
+} |
+ |
+/* A helper that walks over all matching symtabs in all objfiles and |
+ calls CALLBACK for each symbol matching NAME. If SEARCH_PSPACE is |
+ not NULL, then the search is restricted to just that program |
+ space. If INCLUDE_INLINE is nonzero then symbols representing |
+ inlined instances of functions will be included in the result. */ |
+ |
+static void |
+iterate_over_all_matching_symtabs (struct linespec_state *state, |
+ const char *name, |
+ const domain_enum domain, |
+ symbol_found_callback_ftype *callback, |
+ void *data, |
+ struct program_space *search_pspace, |
+ int include_inline) |
+{ |
+ struct objfile *objfile; |
+ struct program_space *pspace; |
+ struct symbol_matcher_data matcher_data; |
+ |
+ matcher_data.lookup_name = name; |
+ matcher_data.symbol_name_cmp = |
+ state->language->la_get_symbol_name_cmp != NULL |
+ ? state->language->la_get_symbol_name_cmp (name) |
+ : strcmp_iw; |
+ |
+ ALL_PSPACES (pspace) |
+ { |
+ if (search_pspace != NULL && search_pspace != pspace) |
+ continue; |
+ if (pspace->executing_startup) |
+ continue; |
+ |
+ set_current_program_space (pspace); |
+ |
+ ALL_OBJFILES (objfile) |
+ { |
+ struct symtab *symtab; |
+ |
+ if (objfile->sf) |
+ objfile->sf->qf->expand_symtabs_matching (objfile, NULL, |
+ iterate_name_matcher, |
+ ALL_DOMAIN, |
+ &matcher_data); |
+ |
+ ALL_OBJFILE_PRIMARY_SYMTABS (objfile, symtab) |
+ { |
+ struct block *block; |
+ |
+ block = BLOCKVECTOR_BLOCK (BLOCKVECTOR (symtab), STATIC_BLOCK); |
+ LA_ITERATE_OVER_SYMBOLS (block, name, domain, callback, data); |
+ |
+ if (include_inline) |
+ { |
+ struct symbol_and_data_callback cad = { callback, data }; |
+ int i; |
+ |
+ for (i = FIRST_LOCAL_BLOCK; |
+ i < BLOCKVECTOR_NBLOCKS (BLOCKVECTOR (symtab)); i++) |
+ { |
+ block = BLOCKVECTOR_BLOCK (BLOCKVECTOR (symtab), i); |
+ LA_ITERATE_OVER_SYMBOLS (block, name, domain, |
+ iterate_inline_only, &cad); |
+ } |
+ } |
+ } |
+ } |
+ } |
+} |
+ |
+/* Returns the block to be used for symbol searches for the given SYMTAB, |
+ which may be NULL. */ |
+ |
+static struct block * |
+get_search_block (struct symtab *symtab) |
+{ |
+ struct block *block; |
+ |
+ if (symtab != NULL) |
+ block = BLOCKVECTOR_BLOCK (BLOCKVECTOR (symtab), STATIC_BLOCK); |
+ else |
+ { |
+ enum language save_language; |
+ |
+ /* get_selected_block can change the current language when there is |
+ no selected frame yet. */ |
+ save_language = current_language->la_language; |
+ block = get_selected_block (0); |
+ set_language (save_language); |
+ } |
+ |
+ return block; |
+} |
+ |
+/* A helper for find_method. This finds all methods in type T which |
+ match NAME. It adds matching symbol names to RESULT_NAMES, and |
+ adds T's direct superclasses to SUPERCLASSES. */ |
+ |
+static void |
+find_methods (struct type *t, const char *name, |
+ VEC (const_char_ptr) **result_names, |
+ VEC (typep) **superclasses) |
+{ |
+ int i1 = 0; |
+ int ibase; |
+ const char *class_name = type_name_no_tag (t); |
+ |
+ /* Ignore this class if it doesn't have a name. This is ugly, but |
unless we figure out how to get the physname without the name of |
the class, then the loop can't do any good. */ |
if (class_name) |
@@ -439,7 +1091,7 @@ find_methods (struct type *t, const char *name, |
method_counter >= 0; |
--method_counter) |
{ |
- char *method_name = TYPE_FN_FIELDLIST_NAME (t, method_counter); |
+ const char *method_name = TYPE_FN_FIELDLIST_NAME (t, method_counter); |
char dem_opname[64]; |
if (strncmp (method_name, "__", 2) == 0 || |
@@ -483,14 +1135,14 @@ find_methods (struct type *t, const char *name, |
strings. Also, ignore the char within a template name, like a ',' |
within foo<int, int>. */ |
-static char * |
-find_toplevel_char (char *s, char c) |
+static 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. */ |
- char *scan; |
+ const char *scan; |
for (scan = s; *scan; scan++) |
{ |
@@ -514,23 +1166,34 @@ find_toplevel_char (char *s, char c) |
return 0; |
} |
-/* Determines if the gives string corresponds to an Objective-C method |
- representation, such as -[Foo bar:] or +[Foo bar]. Objective-C symbols |
- are allowed to have spaces and parentheses in them. */ |
+/* The string equivalent of find_toplevel_char. Returns a pointer |
+ to the location of NEEDLE in HAYSTACK, ignoring any occurrences |
+ inside "()" and "<>". Returns NULL if NEEDLE was not found. */ |
-static int |
-is_objc_method_format (const char *s) |
+static const char * |
+find_toplevel_string (const char *haystack, const char *needle) |
{ |
- if (s == NULL || *s == '\0') |
- return 0; |
- /* Handle arguments with the format FILENAME:SYMBOL. */ |
- if ((s[0] == ':') && (strchr ("+-", s[1]) != NULL) |
- && (s[2] == '[') && strchr(s, ']')) |
- return 1; |
- /* Handle arguments that are just SYMBOL. */ |
- else if ((strchr ("+-", s[0]) != NULL) && (s[1] == '[') && strchr(s, ']')) |
- return 1; |
- return 0; |
+ const char *s = haystack; |
+ |
+ do |
+ { |
+ s = find_toplevel_char (s, *needle); |
+ |
+ if (s != NULL) |
+ { |
+ /* Found first char in HAYSTACK; check rest of string. */ |
+ if (strncmp (s, needle, strlen (needle)) == 0) |
+ return s; |
+ |
+ /* Didn't find it; loop over HAYSTACK, looking for the next |
+ instance of the first character of NEEDLE. */ |
+ ++s; |
+ } |
+ } |
+ while (s != NULL && *s != '\0'); |
+ |
+ /* NEEDLE was not found in HAYSTACK. */ |
+ return NULL; |
} |
/* Given FILTERS, a list of canonical names, filter the sals in RESULT |
@@ -634,178 +1297,690 @@ decode_line_2 (struct linespec_state *self, |
return; |
} |
- printf_unfiltered (_("[0] cancel\n[1] all\n")); |
- for (i = 0; VEC_iterate (const_char_ptr, item_names, i, iter); ++i) |
- printf_unfiltered ("[%d] %s\n", i + 2, iter); |
+ /* Sort the list of method names alphabetically. */ |
+ qsort (VEC_address (const_char_ptr, item_names), |
+ VEC_length (const_char_ptr, item_names), |
+ sizeof (const_char_ptr), compare_strings); |
+ |
+ printf_unfiltered (_("[0] cancel\n[1] all\n")); |
+ for (i = 0; VEC_iterate (const_char_ptr, item_names, i, iter); ++i) |
+ printf_unfiltered ("[%d] %s\n", i + 2, iter); |
+ |
+ prompt = getenv ("PS2"); |
+ if (prompt == NULL) |
+ { |
+ prompt = "> "; |
+ } |
+ args = command_line_input (prompt, 0, "overload-choice"); |
+ |
+ if (args == 0 || *args == 0) |
+ error_no_arg (_("one or more choice numbers")); |
+ |
+ init_number_or_range (&state, args); |
+ while (!state.finished) |
+ { |
+ int num; |
+ |
+ num = get_number_or_range (&state); |
+ |
+ if (num == 0) |
+ error (_("canceled")); |
+ else if (num == 1) |
+ { |
+ /* We intentionally make this result in a single breakpoint, |
+ contrary to what older versions of gdb did. The |
+ rationale is that this lets a user get the |
+ multiple_symbols_all behavior even with the 'ask' |
+ setting; and he can get separate breakpoints by entering |
+ "2-57" at the query. */ |
+ do_cleanups (old_chain); |
+ convert_results_to_lsals (self, result); |
+ return; |
+ } |
+ |
+ num -= 2; |
+ if (num >= VEC_length (const_char_ptr, item_names)) |
+ printf_unfiltered (_("No choice number %d.\n"), num); |
+ else |
+ { |
+ const char *elt = VEC_index (const_char_ptr, item_names, num); |
+ |
+ if (elt != NULL) |
+ { |
+ VEC_safe_push (const_char_ptr, filters, elt); |
+ VEC_replace (const_char_ptr, item_names, num, NULL); |
+ } |
+ else |
+ { |
+ printf_unfiltered (_("duplicate request for %d ignored.\n"), |
+ num); |
+ } |
+ } |
+ } |
+ |
+ filter_results (self, result, filters); |
+ do_cleanups (old_chain); |
+} |
+ |
+ |
+ |
+/* The parser of linespec itself. */ |
+ |
+/* Throw an appropriate error when SYMBOL is not found (optionally in |
+ FILENAME). */ |
+ |
+static void ATTRIBUTE_NORETURN |
+symbol_not_found_error (char *symbol, char *filename) |
+{ |
+ if (symbol == NULL) |
+ symbol = ""; |
+ |
+ if (!have_full_symbols () |
+ && !have_partial_symbols () |
+ && !have_minimal_symbols ()) |
+ throw_error (NOT_FOUND_ERROR, |
+ _("No symbol table is loaded. Use the \"file\" command.")); |
+ |
+ /* If SYMBOL starts with '$', the user attempted to either lookup |
+ a function/variable in his code starting with '$' or an internal |
+ variable of that name. Since we do not know which, be concise and |
+ explain both possibilities. */ |
+ if (*symbol == '$') |
+ { |
+ if (filename) |
+ throw_error (NOT_FOUND_ERROR, |
+ _("Undefined convenience variable or function \"%s\" " |
+ "not defined in \"%s\"."), symbol, filename); |
+ else |
+ throw_error (NOT_FOUND_ERROR, |
+ _("Undefined convenience variable or function \"%s\" " |
+ "not defined."), symbol); |
+ } |
+ else |
+ { |
+ if (filename) |
+ throw_error (NOT_FOUND_ERROR, |
+ _("Function \"%s\" not defined in \"%s\"."), |
+ symbol, filename); |
+ else |
+ throw_error (NOT_FOUND_ERROR, |
+ _("Function \"%s\" not defined."), symbol); |
+ } |
+} |
+ |
+/* Throw an appropriate error when an unexpected token is encountered |
+ in the input. */ |
+ |
+static void ATTRIBUTE_NORETURN |
+unexpected_linespec_error (linespec_parser *parser) |
+{ |
+ linespec_token token; |
+ static const char * token_type_strings[] |
+ = {"keyword", "colon", "string", "number", "comma", "end of input"}; |
+ |
+ /* Get the token that generated the error. */ |
+ token = linespec_lexer_lex_one (parser); |
+ |
+ /* Finally, throw the error. */ |
+ if (token.type == LSTOKEN_STRING || token.type == LSTOKEN_NUMBER |
+ || token.type == LSTOKEN_KEYWORD) |
+ { |
+ char *string; |
+ struct cleanup *cleanup; |
+ |
+ string = copy_token_string (token); |
+ cleanup = make_cleanup (xfree, string); |
+ throw_error (GENERIC_ERROR, |
+ _("malformed linespec error: unexpected %s, \"%s\""), |
+ token_type_strings[token.type], string); |
+ } |
+ else |
+ throw_error (GENERIC_ERROR, |
+ _("malformed linespec error: unexpected %s"), |
+ token_type_strings[token.type]); |
+} |
+ |
+/* Parse and return a line offset in STRING. */ |
+ |
+static struct line_offset |
+linespec_parse_line_offset (char *string) |
+{ |
+ struct line_offset line_offset = {0, LINE_OFFSET_NONE}; |
+ |
+ if (*string == '+') |
+ { |
+ line_offset.sign = LINE_OFFSET_PLUS; |
+ ++string; |
+ } |
+ else if (*string == '-') |
+ { |
+ line_offset.sign = LINE_OFFSET_MINUS; |
+ ++string; |
+ } |
+ |
+ /* Right now, we only allow base 10 for offsets. */ |
+ line_offset.offset = atoi (string); |
+ return line_offset; |
+} |
+ |
+/* Parse the basic_spec in PARSER's input. */ |
+ |
+static void |
+linespec_parse_basic (linespec_parser *parser) |
+{ |
+ char *name; |
+ linespec_token token; |
+ VEC (symbolp) *symbols, *labels; |
+ VEC (minsym_and_objfile_d) *minimal_symbols; |
+ struct cleanup *cleanup; |
+ |
+ /* Get the next token. */ |
+ token = linespec_lexer_lex_one (parser); |
+ |
+ /* If it is EOI or KEYWORD, issue an error. */ |
+ if (token.type == LSTOKEN_KEYWORD || token.type == LSTOKEN_EOI) |
+ unexpected_linespec_error (parser); |
+ /* If it is a LSTOKEN_NUMBER, we have an offset. */ |
+ else if (token.type == LSTOKEN_NUMBER) |
+ { |
+ /* Record the line offset and get the next token. */ |
+ name = copy_token_string (token); |
+ cleanup = make_cleanup (xfree, name); |
+ PARSER_RESULT (parser)->line_offset = linespec_parse_line_offset (name); |
+ do_cleanups (cleanup); |
+ |
+ /* Get the next token. */ |
+ token = linespec_lexer_consume_token (parser); |
+ |
+ /* If the next token is a comma, stop parsing and return. */ |
+ if (token.type == LSTOKEN_COMMA) |
+ return; |
+ |
+ /* If the next token is anything but EOI or KEYWORD, issue |
+ an error. */ |
+ if (token.type != LSTOKEN_KEYWORD && token.type != LSTOKEN_EOI) |
+ unexpected_linespec_error (parser); |
+ } |
+ |
+ if (token.type == LSTOKEN_KEYWORD || token.type == LSTOKEN_EOI) |
+ return; |
+ |
+ /* Next token must be LSTOKEN_STRING. */ |
+ if (token.type != LSTOKEN_STRING) |
+ unexpected_linespec_error (parser); |
+ |
+ /* The current token will contain the name of a function, method, |
+ or label. */ |
+ name = copy_token_string (token); |
+ cleanup = make_cleanup (xfree, name); |
+ |
+ /* Try looking it up as a function/method. */ |
+ find_linespec_symbols (PARSER_STATE (parser), |
+ PARSER_RESULT (parser)->file_symtabs, name, |
+ &symbols, &minimal_symbols); |
+ |
+ if (symbols != NULL || minimal_symbols != NULL) |
+ { |
+ PARSER_RESULT (parser)->function_symbols = symbols; |
+ PARSER_RESULT (parser)->minimal_symbols = minimal_symbols; |
+ PARSER_RESULT (parser)->function_name = name; |
+ symbols = NULL; |
+ discard_cleanups (cleanup); |
+ } |
+ else |
+ { |
+ /* NAME was not a function or a method. So it must be a label |
+ name. */ |
+ labels = find_label_symbols (PARSER_STATE (parser), NULL, |
+ &symbols, name); |
+ if (labels != NULL) |
+ { |
+ PARSER_RESULT (parser)->labels.label_symbols = labels; |
+ PARSER_RESULT (parser)->labels.function_symbols = symbols; |
+ PARSER_RESULT (parser)->label_name = name; |
+ symbols = NULL; |
+ discard_cleanups (cleanup); |
+ } |
+ else |
+ { |
+ /* The name is also not a label. Abort parsing. Do not throw |
+ an error here. parse_linespec will do it for us. */ |
+ |
+ /* Save a copy of the name we were trying to lookup. */ |
+ PARSER_RESULT (parser)->function_name = name; |
+ discard_cleanups (cleanup); |
+ return; |
+ } |
+ } |
+ |
+ /* Get the next token. */ |
+ token = linespec_lexer_consume_token (parser); |
+ |
+ if (token.type == LSTOKEN_COLON) |
+ { |
+ /* User specified a label or a lineno. */ |
+ token = linespec_lexer_consume_token (parser); |
+ |
+ if (token.type == LSTOKEN_NUMBER) |
+ { |
+ /* User specified an offset. Record the line offset and |
+ get the next token. */ |
+ name = copy_token_string (token); |
+ cleanup = make_cleanup (xfree, name); |
+ PARSER_RESULT (parser)->line_offset |
+ = linespec_parse_line_offset (name); |
+ do_cleanups (cleanup); |
+ |
+ /* Ge the next token. */ |
+ token = linespec_lexer_consume_token (parser); |
+ } |
+ else if (token.type == LSTOKEN_STRING) |
+ { |
+ /* Grab a copy of the label's name and look it up. */ |
+ name = copy_token_string (token); |
+ cleanup = make_cleanup (xfree, name); |
+ labels = find_label_symbols (PARSER_STATE (parser), |
+ PARSER_RESULT (parser)->function_symbols, |
+ &symbols, name); |
+ |
+ if (labels != NULL) |
+ { |
+ PARSER_RESULT (parser)->labels.label_symbols = labels; |
+ PARSER_RESULT (parser)->labels.function_symbols = symbols; |
+ PARSER_RESULT (parser)->label_name = name; |
+ symbols = NULL; |
+ discard_cleanups (cleanup); |
+ } |
+ else |
+ { |
+ /* We don't know what it was, but it isn't a label. */ |
+ throw_error (NOT_FOUND_ERROR, |
+ _("No label \"%s\" defined in function \"%s\"."), |
+ name, PARSER_RESULT (parser)->function_name); |
+ } |
+ |
+ /* Check for a line offset. */ |
+ token = linespec_lexer_consume_token (parser); |
+ if (token.type == LSTOKEN_COLON) |
+ { |
+ /* Get the next token. */ |
+ token = linespec_lexer_consume_token (parser); |
+ |
+ /* It must be a line offset. */ |
+ if (token.type != LSTOKEN_NUMBER) |
+ unexpected_linespec_error (parser); |
- prompt = getenv ("PS2"); |
- if (prompt == NULL) |
- { |
- prompt = "> "; |
+ /* Record the lione offset and get the next token. */ |
+ name = copy_token_string (token); |
+ cleanup = make_cleanup (xfree, name); |
+ |
+ PARSER_RESULT (parser)->line_offset |
+ = linespec_parse_line_offset (name); |
+ do_cleanups (cleanup); |
+ |
+ /* Get the next token. */ |
+ token = linespec_lexer_consume_token (parser); |
+ } |
+ } |
+ else |
+ { |
+ /* Trailing ':' in the input. Issue an error. */ |
+ unexpected_linespec_error (parser); |
+ } |
} |
- args = command_line_input (prompt, 0, "overload-choice"); |
+} |
- if (args == 0 || *args == 0) |
- error_no_arg (_("one or more choice numbers")); |
+/* Canonicalize the linespec contained in LS. The result is saved into |
+ STATE->canonical. */ |
- init_number_or_range (&state, args); |
- while (!state.finished) |
+static void |
+canonicalize_linespec (struct linespec_state *state, linespec_p ls) |
+{ |
+ /* If canonicalization was not requested, no need to do anything. */ |
+ if (!state->canonical) |
+ return; |
+ |
+ /* Shortcut expressions, which can only appear by themselves. */ |
+ if (ls->expression != NULL) |
+ state->canonical->addr_string = xstrdup (ls->expression); |
+ else |
{ |
- int num; |
+ struct ui_file *buf; |
+ int need_colon = 0; |
- num = get_number_or_range (&state); |
+ buf = mem_fileopen (); |
+ if (ls->source_filename) |
+ { |
+ fputs_unfiltered (ls->source_filename, buf); |
+ need_colon = 1; |
+ } |
- if (num == 0) |
- error (_("canceled")); |
- else if (num == 1) |
+ if (ls->function_name) |
{ |
- /* We intentionally make this result in a single breakpoint, |
- contrary to what older versions of gdb did. The |
- rationale is that this lets a user get the |
- multiple_symbols_all behavior even with the 'ask' |
- setting; and he can get separate breakpoints by entering |
- "2-57" at the query. */ |
- do_cleanups (old_chain); |
- convert_results_to_lsals (self, result); |
- return; |
+ if (need_colon) |
+ fputc_unfiltered (':', buf); |
+ fputs_unfiltered (ls->function_name, buf); |
+ need_colon = 1; |
} |
- num -= 2; |
- if (num >= VEC_length (const_char_ptr, item_names)) |
- printf_unfiltered (_("No choice number %d.\n"), num); |
- else |
+ if (ls->label_name) |
{ |
- const char *elt = VEC_index (const_char_ptr, item_names, num); |
+ if (need_colon) |
+ fputc_unfiltered (':', buf); |
- if (elt != NULL) |
- { |
- VEC_safe_push (const_char_ptr, filters, elt); |
- VEC_replace (const_char_ptr, item_names, num, NULL); |
- } |
- else |
+ if (ls->function_name == NULL) |
{ |
- printf_unfiltered (_("duplicate request for %d ignored.\n"), |
- num); |
+ struct symbol *s; |
+ |
+ /* No function was specified, so add the symbol name. */ |
+ gdb_assert (ls->labels.function_symbols != NULL |
+ && (VEC_length (symbolp, ls->labels.function_symbols) |
+ == 1)); |
+ s = VEC_index (symbolp, ls->labels.function_symbols, 0); |
+ fputs_unfiltered (SYMBOL_NATURAL_NAME (s), buf); |
+ fputc_unfiltered (':', buf); |
} |
+ |
+ fputs_unfiltered (ls->label_name, buf); |
+ need_colon = 1; |
+ state->canonical->special_display = 1; |
} |
- } |
- filter_results (self, result, filters); |
- do_cleanups (old_chain); |
+ if (ls->line_offset.sign != LINE_OFFSET_UNKNOWN) |
+ { |
+ if (need_colon) |
+ fputc_unfiltered (':', buf); |
+ fprintf_filtered (buf, "%s%d", |
+ (ls->line_offset.sign == LINE_OFFSET_NONE ? "" |
+ : (ls->line_offset.sign |
+ == LINE_OFFSET_PLUS ? "+" : "-")), |
+ ls->line_offset.offset); |
+ } |
+ |
+ state->canonical->addr_string = ui_file_xstrdup (buf, NULL); |
+ ui_file_delete (buf); |
+ } |
} |
-/* Valid delimiters for linespec keywords "if", "thread" or "task". */ |
+/* Given a line offset in LS, construct the relevant SALs. */ |
-static int |
-is_linespec_boundary (char c) |
+static struct symtabs_and_lines |
+create_sals_line_offset (struct linespec_state *self, |
+ linespec_p ls) |
{ |
- return c == ' ' || c == '\t' || c == '\0' || c == ','; |
-} |
+ struct symtabs_and_lines values; |
+ struct symtab_and_line val; |
+ int use_default = 0; |
-/* A helper function for decode_line_1 and friends which skips P |
- past any method overload information at the beginning of P, e.g., |
- "(const struct foo *)". |
+ init_sal (&val); |
+ values.sals = NULL; |
+ values.nelts = 0; |
- This function assumes that P has already been validated to contain |
- overload information, and it will assert if *P != '('. */ |
-static char * |
-find_method_overload_end (char *p) |
-{ |
- int depth = 0; |
+ /* This is where we need to make sure we have good defaults. |
+ We must guarantee that this section of code is never executed |
+ when we are called with just a function anme, since |
+ set_default_source_symtab_and_line uses |
+ select_source_symtab that calls us with such an argument. */ |
- gdb_assert (*p == '('); |
+ if (VEC_length (symtab_p, ls->file_symtabs) == 1 |
+ && VEC_index (symtab_p, ls->file_symtabs, 0) == NULL) |
+ { |
+ set_current_program_space (self->program_space); |
- while (*p) |
+ /* Make sure we have at least a default source line. */ |
+ set_default_source_symtab_and_line (); |
+ initialize_defaults (&self->default_symtab, &self->default_line); |
+ VEC_pop (symtab_p, ls->file_symtabs); |
+ VEC_free (symtab_p, ls->file_symtabs); |
+ ls->file_symtabs |
+ = collect_symtabs_from_filename (self->default_symtab->filename); |
+ use_default = 1; |
+ } |
+ |
+ val.line = ls->line_offset.offset; |
+ switch (ls->line_offset.sign) |
{ |
- if (*p == '(') |
- ++depth; |
- else if (*p == ')') |
+ case LINE_OFFSET_PLUS: |
+ if (ls->line_offset.offset == 0) |
+ val.line = 5; |
+ if (use_default) |
+ val.line = self->default_line + val.line; |
+ break; |
+ |
+ case LINE_OFFSET_MINUS: |
+ if (ls->line_offset.offset == 0) |
+ val.line = 15; |
+ if (use_default) |
+ val.line = self->default_line - val.line; |
+ else |
+ val.line = -val.line; |
+ break; |
+ |
+ case LINE_OFFSET_NONE: |
+ break; /* No need to adjust val.line. */ |
+ } |
+ |
+ if (self->list_mode) |
+ decode_digits_list_mode (self, ls, &values, val); |
+ else |
+ { |
+ struct linetable_entry *best_entry = NULL; |
+ int *filter; |
+ struct block **blocks; |
+ struct cleanup *cleanup; |
+ struct symtabs_and_lines intermediate_results; |
+ int i, j; |
+ |
+ intermediate_results.sals = NULL; |
+ intermediate_results.nelts = 0; |
+ |
+ decode_digits_ordinary (self, ls, val.line, &intermediate_results, |
+ &best_entry); |
+ if (intermediate_results.nelts == 0 && best_entry != NULL) |
+ decode_digits_ordinary (self, ls, best_entry->line, |
+ &intermediate_results, &best_entry); |
+ |
+ cleanup = make_cleanup (xfree, intermediate_results.sals); |
+ |
+ /* For optimized code, the compiler can scatter one source line |
+ across disjoint ranges of PC values, even when no duplicate |
+ functions or inline functions are involved. For example, |
+ 'for (;;)' inside a non-template, non-inline, and non-ctor-or-dtor |
+ function can result in two PC ranges. In this case, we don't |
+ want to set a breakpoint on the first PC of each range. To filter |
+ such cases, we use containing blocks -- for each PC found |
+ above, we see if there are other PCs that are in the same |
+ block. If yes, the other PCs are filtered out. */ |
+ |
+ filter = XNEWVEC (int, intermediate_results.nelts); |
+ make_cleanup (xfree, filter); |
+ blocks = XNEWVEC (struct block *, intermediate_results.nelts); |
+ make_cleanup (xfree, blocks); |
+ |
+ for (i = 0; i < intermediate_results.nelts; ++i) |
{ |
- if (--depth == 0) |
- { |
- ++p; |
- break; |
- } |
+ set_current_program_space (intermediate_results.sals[i].pspace); |
+ |
+ filter[i] = 1; |
+ blocks[i] = block_for_pc_sect (intermediate_results.sals[i].pc, |
+ intermediate_results.sals[i].section); |
} |
- ++p; |
+ |
+ for (i = 0; i < intermediate_results.nelts; ++i) |
+ { |
+ if (blocks[i] != NULL) |
+ for (j = i + 1; j < intermediate_results.nelts; ++j) |
+ { |
+ if (blocks[j] == blocks[i]) |
+ { |
+ filter[j] = 0; |
+ break; |
+ } |
+ } |
+ } |
+ |
+ for (i = 0; i < intermediate_results.nelts; ++i) |
+ if (filter[i]) |
+ { |
+ struct symbol *sym = (blocks[i] |
+ ? block_containing_function (blocks[i]) |
+ : NULL); |
+ |
+ if (self->funfirstline) |
+ skip_prologue_sal (&intermediate_results.sals[i]); |
+ /* Make sure the line matches the request, not what was |
+ found. */ |
+ intermediate_results.sals[i].line = val.line; |
+ add_sal_to_sals (self, &values, &intermediate_results.sals[i], |
+ sym ? SYMBOL_NATURAL_NAME (sym) : NULL); |
+ } |
+ |
+ do_cleanups (cleanup); |
} |
- return p; |
+ if (values.nelts == 0) |
+ { |
+ if (ls->source_filename) |
+ throw_error (NOT_FOUND_ERROR, _("No line %d in file \"%s\"."), |
+ val.line, ls->source_filename); |
+ else |
+ throw_error (NOT_FOUND_ERROR, _("No line %d in the current file."), |
+ val.line); |
+ } |
+ |
+ return values; |
} |
-/* Keep important information used when looking up a name. This includes |
- template parameters, overload information, and important keywords, including |
- the possible Java trailing type. */ |
+/* Create and return SALs from the linespec LS. */ |
-static char * |
-keep_name_info (char *p, int on_boundary) |
+static struct symtabs_and_lines |
+convert_linespec_to_sals (struct linespec_state *state, linespec_p ls) |
{ |
- const char *quotes = get_gdb_completer_quote_characters (); |
- char *saved_p = p; |
- int nest = 0; |
+ struct symtabs_and_lines sals = {NULL, 0}; |
- while (*p) |
+ if (ls->expression != NULL) |
{ |
- if (strchr (quotes, *p)) |
- break; |
- |
- if (*p == ',' && !nest) |
- break; |
+ /* We have an expression. No other attribute is allowed. */ |
+ sals.sals = XMALLOC (struct symtab_and_line); |
+ sals.nelts = 1; |
+ sals.sals[0] = find_pc_line (ls->expr_pc, 0); |
+ sals.sals[0].pc = ls->expr_pc; |
+ sals.sals[0].section = find_pc_overlay (ls->expr_pc); |
+ sals.sals[0].explicit_pc = 1; |
+ } |
+ else if (ls->labels.label_symbols != NULL) |
+ { |
+ /* We have just a bunch of functions/methods or labels. */ |
+ int i; |
+ struct symtab_and_line sal; |
+ struct symbol *sym; |
- if (on_boundary && !nest) |
+ for (i = 0; VEC_iterate (symbolp, ls->labels.label_symbols, i, sym); ++i) |
{ |
- const char *const words[] = { "if", "thread", "task" }; |
- int wordi; |
- |
- for (wordi = 0; wordi < ARRAY_SIZE (words); wordi++) |
- if (strncmp (p, words[wordi], strlen (words[wordi])) == 0 |
- && is_linespec_boundary (p[strlen (words[wordi])])) |
- break; |
- if (wordi < ARRAY_SIZE (words)) |
- break; |
+ if (symbol_to_sal (&sal, state->funfirstline, sym)) |
+ add_sal_to_sals (state, &sals, &sal, |
+ SYMBOL_NATURAL_NAME (sym)); |
} |
+ } |
+ else if (ls->function_symbols != NULL || ls->minimal_symbols != NULL) |
+ { |
+ /* We have just a bunch of functions and/or methods. */ |
+ int i; |
+ struct symtab_and_line sal; |
+ struct symbol *sym; |
+ minsym_and_objfile_d *elem; |
+ struct program_space *pspace; |
- if (*p == '(' || *p == '<' || *p == '[') |
- nest++; |
- else if ((*p == ')' || *p == '>' || *p == ']') && nest > 0) |
- nest--; |
+ if (ls->function_symbols != NULL) |
+ { |
+ /* Sort symbols so that symbols with the same program space are next |
+ to each other. */ |
+ qsort (VEC_address (symbolp, ls->function_symbols), |
+ VEC_length (symbolp, ls->function_symbols), |
+ sizeof (symbolp), compare_symbols); |
- p++; |
+ for (i = 0; VEC_iterate (symbolp, ls->function_symbols, i, sym); ++i) |
+ { |
+ pspace = SYMTAB_PSPACE (SYMBOL_SYMTAB (sym)); |
+ set_current_program_space (pspace); |
+ if (symbol_to_sal (&sal, state->funfirstline, sym) |
+ && maybe_add_address (state->addr_set, pspace, sal.pc)) |
+ add_sal_to_sals (state, &sals, &sal, SYMBOL_NATURAL_NAME (sym)); |
+ } |
+ } |
- /* The ',' check could fail on "operator ,". */ |
- p += cp_validate_operator (p); |
+ if (ls->minimal_symbols != NULL) |
+ { |
+ /* Sort minimal symbols by program space, too. */ |
+ qsort (VEC_address (minsym_and_objfile_d, ls->minimal_symbols), |
+ VEC_length (minsym_and_objfile_d, ls->minimal_symbols), |
+ sizeof (minsym_and_objfile_d), compare_msymbols); |
+ |
+ for (i = 0; |
+ VEC_iterate (minsym_and_objfile_d, ls->minimal_symbols, i, elem); |
+ ++i) |
+ { |
+ pspace = elem->objfile->pspace; |
+ set_current_program_space (pspace); |
+ minsym_found (state, elem->objfile, elem->minsym, &sals); |
+ } |
+ } |
+ } |
+ else if (ls->line_offset.sign != LINE_OFFSET_UNKNOWN) |
+ { |
+ /* Only an offset was specified. */ |
+ sals = create_sals_line_offset (state, ls); |
- on_boundary = is_linespec_boundary (p[-1]); |
+ /* Make sure we have a filename for canonicalization. */ |
+ if (ls->source_filename == NULL) |
+ ls->source_filename = xstrdup (state->default_symtab->filename); |
+ } |
+ else |
+ { |
+ /* We haven't found any results... */ |
+ return sals; |
} |
- while (p > saved_p && is_linespec_boundary (p[-1])) |
- p--; |
+ canonicalize_linespec (state, ls); |
- return p; |
-} |
+ if (sals.nelts > 0 && state->canonical != NULL) |
+ state->canonical->pre_expanded = 1; |
- |
-/* The parser of linespec itself. */ |
+ return sals; |
+} |
-/* Parse a string that specifies a line number. |
+/* Parse a string that specifies a linespec. |
Pass the address of a char * variable; that variable will be |
advanced over the characters actually parsed. |
- The string can be: |
+ The basic grammar of linespecs: |
+ |
+ linespec -> expr_spec | var_spec | basic_spec |
+ expr_spec -> '*' STRING |
+ var_spec -> '$' (STRING | NUMBER) |
- LINENUM -- that line number in current file. PC returned is 0. |
- FILE:LINENUM -- that line in that file. PC returned is 0. |
- FUNCTION -- line number of openbrace of that function. |
- PC returned is the start of the function. |
- LABEL -- a label in the current scope |
- VARIABLE -- line number of definition of that variable. |
- PC returned is 0. |
- FILE:FUNCTION -- likewise, but prefer functions in that file. |
- *EXPR -- line in which address EXPR appears. |
+ basic_spec -> file_offset_spec | function_spec | label_spec |
+ file_offset_spec -> opt_file_spec offset_spec |
+ function_spec -> opt_file_spec function_name_spec opt_label_spec |
+ label_spec -> label_name_spec |
- This may all be followed by an "if EXPR", which we ignore. |
+ opt_file_spec -> "" | file_name_spec ':' |
+ opt_label_spec -> "" | ':' label_name_spec |
- FUNCTION may be an undebuggable function found in minimal symbol table. |
+ file_name_spec -> STRING |
+ function_name_spec -> STRING |
+ label_name_spec -> STRING |
+ function_name_spec -> STRING |
+ offset_spec -> NUMBER |
+ -> '+' NUMBER |
+ -> '-' NUMBER |
+ |
+ This may all be followed by several keywords such as "if EXPR", |
+ which we ignore. |
+ |
+ A comma will terminate parsing. |
+ |
+ The function may be an undebuggable function found in minimal symbol table. |
If the argument FUNFIRSTLINE is nonzero, we want the first line |
of real code inside a function when a function is specified, and it is |
@@ -825,319 +2000,215 @@ keep_name_info (char *p, int on_boundary) |
if no file is validly specified. Callers must check that. |
Also, the line number returned may be invalid. */ |
-/* We allow single quotes in various places. This is a hideous |
- kludge, which exists because the completer can't yet deal with the |
- lack of single quotes. FIXME: write a linespec_completer which we |
- can use as appropriate instead of make_symbol_completion_list. */ |
+/* Parse the linespec in ARGPTR. */ |
-struct symtabs_and_lines |
-decode_line_internal (struct linespec_state *self, char **argptr) |
+static struct symtabs_and_lines |
+parse_linespec (linespec_parser *parser, char **argptr) |
{ |
- char *p; |
- char *q; |
- |
- char *copy; |
- /* This says whether or not something in *ARGPTR is quoted with |
- completer_quotes (i.e. with single quotes). */ |
- int is_quoted; |
- /* Is *ARGPTR enclosed in double quotes? */ |
- int is_quote_enclosed; |
- int is_objc_method = 0; |
- char *saved_arg = *argptr; |
- /* If IS_QUOTED, the end of the quoted bit. */ |
- char *end_quote = NULL; |
- /* Is *ARGPTR enclosed in single quotes? */ |
- int is_squote_enclosed = 0; |
- /* The "first half" of the linespec. */ |
- char *first_half; |
- |
- /* If we are parsing `function:label', this holds the symbols |
- matching the function name. */ |
- VEC (symbolp) *function_symbols = NULL; |
- /* If FUNCTION_SYMBOLS is not NULL, then this is the exception that |
- was thrown when trying to parse a filename. */ |
+ linespec_token token; |
+ struct symtabs_and_lines values; |
volatile struct gdb_exception file_exception; |
+ struct cleanup *cleanup; |
- struct cleanup *cleanup = make_cleanup (null_cleanup, NULL); |
- |
- /* Defaults have defaults. */ |
- |
- initialize_defaults (&self->default_symtab, &self->default_line); |
- |
- /* See if arg is *PC. */ |
- |
- if (**argptr == '*') |
- { |
- do_cleanups (cleanup); |
- return decode_indirect (self, argptr); |
- } |
- |
- is_quoted = (strchr (get_gdb_completer_quote_characters (), |
- **argptr) != NULL); |
- |
- if (is_quoted) |
- { |
- end_quote = skip_quoted (*argptr); |
- if (*end_quote == '\0') |
- is_squote_enclosed = 1; |
- } |
- |
- /* Check to see if it's a multipart linespec (with colons or |
- periods). */ |
- |
- /* Locate the end of the first half of the linespec. |
- After the call, for instance, if the argptr string is "foo.c:123" |
- p will point at "123". If there is only one part, like "foo", p |
- will point to "". If this is a C++ name, like "A::B::foo", p will |
- point to "::B::foo". Argptr is not changed by this call. */ |
- |
- first_half = p = locate_first_half (argptr, &is_quote_enclosed); |
- |
- /* First things first: if ARGPTR starts with a filename, get its |
- symtab and strip the filename from ARGPTR. */ |
- TRY_CATCH (file_exception, RETURN_MASK_ERROR) |
- { |
- self->file_symtabs = symtabs_from_filename (argptr, p, is_quote_enclosed, |
- &self->user_filename); |
- } |
- |
- if (VEC_empty (symtab_p, self->file_symtabs)) |
- { |
- /* A NULL entry means to use GLOBAL_DEFAULT_SYMTAB. */ |
- VEC_safe_push (symtab_p, self->file_symtabs, NULL); |
- } |
- |
- if (file_exception.reason >= 0) |
+ /* A special case to start. It has become quite popular for |
+ IDEs to work around bugs in the previous parser by quoting |
+ the entire linespec, so we attempt to deal with this nicely. */ |
+ parser->is_quote_enclosed = 0; |
+ if (!is_ada_operator (*argptr) |
+ && strchr (linespec_quote_characters, **argptr) != NULL) |
{ |
- /* Check for single quotes on the non-filename part. */ |
- is_quoted = (**argptr |
- && strchr (get_gdb_completer_quote_characters (), |
- **argptr) != NULL); |
- if (is_quoted) |
- end_quote = skip_quoted (*argptr); |
+ const char *end; |
- /* Locate the next "half" of the linespec. */ |
- first_half = p = locate_first_half (argptr, &is_quote_enclosed); |
+ end = skip_quote_char (*argptr + 1, **argptr); |
+ if (end != NULL && is_closing_quote_enclosed (end)) |
+ { |
+ /* Here's the special case. Skip ARGPTR past the initial |
+ quote. */ |
+ ++(*argptr); |
+ parser->is_quote_enclosed = 1; |
+ } |
} |
- /* Check if this is an Objective-C method (anything that starts with |
- a '+' or '-' and a '['). */ |
- if (is_objc_method_format (p)) |
- is_objc_method = 1; |
+ /* A keyword at the start cannot be interpreted as such. |
+ Consider "b thread thread 42". */ |
+ parser->keyword_ok = 0; |
- /* Check if the symbol could be an Objective-C selector. */ |
+ parser->lexer.saved_arg = *argptr; |
+ parser->lexer.stream = argptr; |
+ file_exception.reason = 0; |
- { |
- struct symtabs_and_lines values; |
+ /* Initialize the default symtab and line offset. */ |
+ initialize_defaults (&PARSER_STATE (parser)->default_symtab, |
+ &PARSER_STATE (parser)->default_line); |
- values = decode_objc (self, argptr); |
- if (values.sals != NULL) |
- { |
- do_cleanups (cleanup); |
- return values; |
- } |
- } |
+ /* Objective-C shortcut. */ |
+ values = decode_objc (PARSER_STATE (parser), PARSER_RESULT (parser), argptr); |
+ if (values.sals != NULL) |
+ return values; |
+ |
+ /* Start parsing. */ |
- /* Does it look like there actually were two parts? */ |
+ /* Get the first token. */ |
+ token = linespec_lexer_lex_one (parser); |
- if (p[0] == ':' || p[0] == '.') |
+ /* It must be either LSTOKEN_STRING or LSTOKEN_NUMBER. */ |
+ if (token.type == LSTOKEN_STRING && *LS_TOKEN_STOKEN (token).ptr == '*') |
{ |
- /* Is it a C++ or Java compound data structure? |
- The check on p[1] == ':' is capturing the case of "::", |
- since p[0]==':' was checked above. |
- Note that the call to decode_compound does everything |
- for us, including the lookup on the symbol table, so we |
- can return now. */ |
- |
- if (p[0] == '.' || p[1] == ':') |
- { |
- struct symtabs_and_lines values; |
- volatile struct gdb_exception ex; |
- char *saved_argptr = *argptr; |
+ char *expr, *copy; |
- if (is_quote_enclosed) |
- ++saved_arg; |
+ /* User specified an expression, *EXPR. */ |
+ copy = expr = copy_token_string (token); |
+ cleanup = make_cleanup (xfree, expr); |
+ PARSER_RESULT (parser)->expr_pc = linespec_expression_to_pc (©); |
+ discard_cleanups (cleanup); |
+ PARSER_RESULT (parser)->expression = expr; |
- /* Initialize it just to avoid a GCC false warning. */ |
- memset (&values, 0, sizeof (values)); |
+ /* This is a little hacky/tricky. If linespec_expression_to_pc |
+ did not evaluate the entire token, then we must find the |
+ string COPY inside the original token buffer. */ |
+ if (*copy != '\0') |
+ { |
+ PARSER_STREAM (parser) = strstr (parser->lexer.saved_arg, copy); |
+ gdb_assert (PARSER_STREAM (parser) != NULL); |
+ } |
- TRY_CATCH (ex, RETURN_MASK_ERROR) |
- { |
- values = decode_compound (self, argptr, saved_arg, p); |
- } |
- if ((is_quoted || is_squote_enclosed) && **argptr == '\'') |
- *argptr = *argptr + 1; |
+ /* Consume the token. */ |
+ linespec_lexer_consume_token (parser); |
- if (ex.reason >= 0) |
- { |
- do_cleanups (cleanup); |
- return values; |
- } |
+ goto convert_to_sals; |
+ } |
+ else if (token.type == LSTOKEN_STRING && *LS_TOKEN_STOKEN (token).ptr == '$') |
+ { |
+ char *var; |
- if (ex.error != NOT_FOUND_ERROR) |
- throw_exception (ex); |
+ /* A NULL entry means to use GLOBAL_DEFAULT_SYMTAB. */ |
+ VEC_safe_push (symtab_p, PARSER_RESULT (parser)->file_symtabs, NULL); |
- *argptr = saved_argptr; |
- } |
- else |
- { |
- /* If there was an exception looking up a specified filename earlier, |
- then check whether we were really given `function:label'. */ |
- if (file_exception.reason < 0) |
- { |
- function_symbols = find_function_symbols (argptr, p, |
- is_quote_enclosed, |
- &self->user_function); |
+ /* User specified a convenience variable or history value. */ |
+ var = copy_token_string (token); |
+ cleanup = make_cleanup (xfree, var); |
+ PARSER_RESULT (parser)->line_offset |
+ = linespec_parse_variable (PARSER_STATE (parser), var); |
- /* If we did not find a function, re-throw the original |
- exception. */ |
- if (!function_symbols) |
- throw_exception (file_exception); |
+ /* If a line_offset wasn't found (VAR is the name of a user |
+ variable/function), then skip to normal symbol processing. */ |
+ if (PARSER_RESULT (parser)->line_offset.sign != LINE_OFFSET_UNKNOWN) |
+ { |
+ discard_cleanups (cleanup); |
- make_cleanup (VEC_cleanup (symbolp), &function_symbols); |
- } |
+ /* Consume this token. */ |
+ linespec_lexer_consume_token (parser); |
- /* Check for single quotes on the non-filename part. */ |
- if (!is_quoted) |
- { |
- is_quoted = (**argptr |
- && strchr (get_gdb_completer_quote_characters (), |
- **argptr) != NULL); |
- if (is_quoted) |
- end_quote = skip_quoted (*argptr); |
- } |
+ goto convert_to_sals; |
} |
+ |
+ do_cleanups (cleanup); |
} |
+ else if (token.type != LSTOKEN_STRING && token.type != LSTOKEN_NUMBER) |
+ unexpected_linespec_error (parser); |
- /* self->file_symtabs holds the specified file symtabs, or 0 if no file |
- specified. |
- If we are parsing `function:symbol', then FUNCTION_SYMBOLS holds the |
- functions before the `:'. |
- arg no longer contains the file name. */ |
+ /* Now we can recognize keywords. */ |
+ parser->keyword_ok = 1; |
- /* If the filename was quoted, we must re-check the quotation. */ |
+ /* Shortcut: If the next token is not LSTOKEN_COLON, we know that |
+ this token cannot represent a filename. */ |
+ token = linespec_lexer_peek_token (parser); |
- if (end_quote == first_half && *end_quote!= '\0') |
+ if (token.type == LSTOKEN_COLON) |
{ |
- is_quoted = (**argptr |
- && strchr (get_gdb_completer_quote_characters (), |
- **argptr) != NULL); |
- if (is_quoted) |
- end_quote = skip_quoted (*argptr); |
- } |
+ char *user_filename; |
- /* Check whether arg is all digits (and sign). */ |
+ /* Get the current token again and extract the filename. */ |
+ token = linespec_lexer_lex_one (parser); |
+ user_filename = copy_token_string (token); |
- q = *argptr; |
- if (*q == '-' || *q == '+') |
- q++; |
- while (*q >= '0' && *q <= '9') |
- q++; |
+ /* Check if the input is a filename. */ |
+ TRY_CATCH (file_exception, RETURN_MASK_ERROR) |
+ { |
+ PARSER_RESULT (parser)->file_symtabs |
+ = symtabs_from_filename (user_filename); |
+ } |
- if (q != *argptr && (*q == 0 || *q == ' ' || *q == '\t' || *q == ',') |
- && function_symbols == NULL) |
- { |
- struct symtabs_and_lines values; |
+ if (file_exception.reason >= 0) |
+ { |
+ /* Symtabs were found for the file. Record the filename. */ |
+ PARSER_RESULT (parser)->source_filename = user_filename; |
- /* We found a token consisting of all digits -- at least one digit. */ |
- values = decode_all_digits (self, argptr, q); |
- do_cleanups (cleanup); |
- return values; |
- } |
+ /* Get the next token. */ |
+ token = linespec_lexer_consume_token (parser); |
- /* Arg token is not digits => try it as a variable name |
- Find the next token (everything up to end or next whitespace). */ |
+ /* This is LSTOKEN_COLON; consume it. */ |
+ linespec_lexer_consume_token (parser); |
+ } |
+ else |
+ { |
+ /* No symtabs found -- discard user_filename. */ |
+ xfree (user_filename); |
- if (**argptr == '$') /* May be a convenience variable. */ |
- /* One or two $ chars possible. */ |
- p = skip_quoted (*argptr + (((*argptr)[1] == '$') ? 2 : 1)); |
- else if (is_quoted || is_squote_enclosed) |
- { |
- p = end_quote; |
- if (p[-1] != '\'') |
- error (_("Unmatched single quote.")); |
+ /* A NULL entry means to use GLOBAL_DEFAULT_SYMTAB. */ |
+ VEC_safe_push (symtab_p, PARSER_RESULT (parser)->file_symtabs, NULL); |
+ } |
} |
- else if (is_objc_method) |
+ /* If the next token is not EOI, KEYWORD, or COMMA, issue an error. */ |
+ else if (token.type != LSTOKEN_EOI && token.type != LSTOKEN_KEYWORD |
+ && token.type != LSTOKEN_COMMA) |
{ |
- /* allow word separators in method names for Obj-C. */ |
- p = skip_quoted_chars (*argptr, NULL, ""); |
+ /* TOKEN is the _next_ token, not the one currently in the parser. |
+ Consuming the token will give the correct error message. */ |
+ linespec_lexer_consume_token (parser); |
+ unexpected_linespec_error (parser); |
} |
else |
{ |
- p = skip_quoted (*argptr); |
- } |
- |
- /* Keep any important naming information. */ |
- p = keep_name_info (p, p == saved_arg || is_linespec_boundary (p[-1])); |
- |
- copy = (char *) alloca (p - *argptr + 1); |
- memcpy (copy, *argptr, p - *argptr); |
- copy[p - *argptr] = '\0'; |
- if (p != *argptr |
- && copy[0] |
- && copy[0] == copy[p - *argptr - 1] |
- && strchr (get_gdb_completer_quote_characters (), copy[0]) != NULL) |
- { |
- copy[p - *argptr - 1] = '\0'; |
- copy++; |
+ /* A NULL entry means to use GLOBAL_DEFAULT_SYMTAB. */ |
+ VEC_safe_push (symtab_p, PARSER_RESULT (parser)->file_symtabs, NULL); |
} |
- else if (is_quoted || is_squote_enclosed) |
- copy[p - *argptr - 1] = '\0'; |
- |
- *argptr = skip_spaces (p); |
- /* If it starts with $: may be a legitimate variable or routine name |
- (e.g. HP-UX millicode routines such as $$dyncall), or it may |
- be history value, or it may be a convenience variable. */ |
+ /* Parse the rest of the linespec. */ |
+ linespec_parse_basic (parser); |
- if (*copy == '$' && function_symbols == NULL) |
+ if (PARSER_RESULT (parser)->function_symbols == NULL |
+ && PARSER_RESULT (parser)->labels.label_symbols == NULL |
+ && PARSER_RESULT (parser)->line_offset.sign == LINE_OFFSET_UNKNOWN |
+ && PARSER_RESULT (parser)->minimal_symbols == NULL) |
{ |
- struct symtabs_and_lines values; |
- |
- values = decode_dollar (self, copy); |
- do_cleanups (cleanup); |
- return values; |
- } |
+ /* The linespec didn't parse. Re-throw the file exception if |
+ there was one. */ |
+ if (file_exception.reason < 0) |
+ throw_exception (file_exception); |
- /* Try the token as a label, but only if no file was specified, |
- because we can only really find labels in the current scope. */ |
- |
- if (VEC_length (symtab_p, self->file_symtabs) == 1 |
- && VEC_index (symtab_p, self->file_symtabs, 0) == NULL) |
- { |
- struct symtabs_and_lines label_result; |
- if (decode_label (self, function_symbols, copy, &label_result)) |
- { |
- do_cleanups (cleanup); |
- return label_result; |
- } |
+ /* Otherwise, the symbol is not found. */ |
+ symbol_not_found_error (PARSER_RESULT (parser)->function_name, |
+ PARSER_RESULT (parser)->source_filename); |
} |
- if (function_symbols) |
- throw_exception (file_exception); |
+ convert_to_sals: |
- /* Look up that token as a variable. |
- If file specified, use that file's per-file block to start with. */ |
+ /* Get the last token and record how much of the input was parsed, |
+ if necessary. */ |
+ token = linespec_lexer_lex_one (parser); |
+ if (token.type != LSTOKEN_EOI && token.type != LSTOKEN_KEYWORD) |
+ PARSER_STREAM (parser) = LS_TOKEN_STOKEN (token).ptr; |
- { |
- struct symtabs_and_lines values; |
+ /* Convert the data in PARSER_RESULT to SALs. */ |
+ values = convert_linespec_to_sals (PARSER_STATE (parser), |
+ PARSER_RESULT (parser)); |
- values = decode_variable (self, copy); |
- do_cleanups (cleanup); |
- return values; |
- } |
+ return values; |
} |
+ |
/* A constructor for linespec_state. */ |
static void |
linespec_state_constructor (struct linespec_state *self, |
- int flags, |
+ int flags, const struct language_defn *language, |
struct symtab *default_symtab, |
int default_line, |
struct linespec_result *canonical) |
{ |
memset (self, 0, sizeof (*self)); |
+ self->language = language; |
self->funfirstline = (flags & DECODE_LINE_FUNFIRSTLINE) ? 1 : 0; |
self->list_mode = (flags & DECODE_LINE_LIST_MODE) ? 1 : 0; |
self->default_symtab = default_symtab; |
@@ -1148,19 +2219,64 @@ linespec_state_constructor (struct linespec_state *self, |
xfree, xcalloc, xfree); |
} |
+/* Initialize a new linespec parser. */ |
+ |
+static void |
+linespec_parser_new (linespec_parser *parser, |
+ int flags, const struct language_defn *language, |
+ struct symtab *default_symtab, |
+ int default_line, |
+ struct linespec_result *canonical) |
+{ |
+ parser->lexer.current.type = LSTOKEN_CONSUMED; |
+ memset (PARSER_RESULT (parser), 0, sizeof (struct linespec)); |
+ PARSER_RESULT (parser)->line_offset.sign = LINE_OFFSET_UNKNOWN; |
+ linespec_state_constructor (PARSER_STATE (parser), flags, language, |
+ default_symtab, default_line, canonical); |
+} |
+ |
/* A destructor for linespec_state. */ |
static void |
-linespec_state_destructor (void *arg) |
+linespec_state_destructor (struct linespec_state *self) |
{ |
- struct linespec_state *self = arg; |
- |
- xfree (self->user_filename); |
- xfree (self->user_function); |
- VEC_free (symtab_p, self->file_symtabs); |
htab_delete (self->addr_set); |
} |
+/* Delete a linespec parser. */ |
+ |
+static void |
+linespec_parser_delete (void *arg) |
+{ |
+ linespec_parser *parser = (linespec_parser *) arg; |
+ |
+ if (PARSER_RESULT (parser)->expression) |
+ xfree (PARSER_RESULT (parser)->expression); |
+ if (PARSER_RESULT (parser)->source_filename) |
+ xfree (PARSER_RESULT (parser)->source_filename); |
+ if (PARSER_RESULT (parser)->label_name) |
+ xfree (PARSER_RESULT (parser)->label_name); |
+ if (PARSER_RESULT (parser)->function_name) |
+ xfree (PARSER_RESULT (parser)->function_name); |
+ |
+ if (PARSER_RESULT (parser)->file_symtabs != NULL) |
+ VEC_free (symtab_p, PARSER_RESULT (parser)->file_symtabs); |
+ |
+ if (PARSER_RESULT (parser)->function_symbols != NULL) |
+ VEC_free (symbolp, PARSER_RESULT (parser)->function_symbols); |
+ |
+ if (PARSER_RESULT (parser)->minimal_symbols != NULL) |
+ VEC_free (minsym_and_objfile_d, PARSER_RESULT (parser)->minimal_symbols); |
+ |
+ if (PARSER_RESULT (parser)->labels.label_symbols != NULL) |
+ VEC_free (symbolp, PARSER_RESULT (parser)->labels.label_symbols); |
+ |
+ if (PARSER_RESULT (parser)->labels.function_symbols != NULL) |
+ VEC_free (symbolp, PARSER_RESULT (parser)->labels.function_symbols); |
+ |
+ linespec_state_destructor (PARSER_STATE (parser)); |
+} |
+ |
/* See linespec.h. */ |
void |
@@ -1171,10 +2287,11 @@ decode_line_full (char **argptr, int flags, |
const char *filter) |
{ |
struct symtabs_and_lines result; |
- struct linespec_state state; |
struct cleanup *cleanups; |
char *arg_start = *argptr; |
VEC (const_char_ptr) *filters = NULL; |
+ linespec_parser parser; |
+ struct linespec_state *state; |
gdb_assert (canonical != NULL); |
/* The filter only makes sense for 'all'. */ |
@@ -1185,12 +2302,13 @@ decode_line_full (char **argptr, int flags, |
|| select_mode == multiple_symbols_cancel); |
gdb_assert ((flags & DECODE_LINE_LIST_MODE) == 0); |
- linespec_state_constructor (&state, flags, |
- default_symtab, default_line, canonical); |
- cleanups = make_cleanup (linespec_state_destructor, &state); |
+ linespec_parser_new (&parser, flags, current_language, default_symtab, |
+ default_line, canonical); |
+ cleanups = make_cleanup (linespec_parser_delete, &parser); |
save_current_program_space (); |
- result = decode_line_internal (&state, argptr); |
+ result = parse_linespec (&parser, argptr); |
+ state = PARSER_STATE (&parser); |
gdb_assert (result.nelts == 1 || canonical->pre_expanded); |
gdb_assert (canonical->addr_string != NULL); |
@@ -1201,15 +2319,15 @@ decode_line_full (char **argptr, int flags, |
{ |
int i; |
- if (state.canonical_names == NULL) |
- state.canonical_names = xcalloc (result.nelts, sizeof (char *)); |
- make_cleanup (xfree, state.canonical_names); |
+ if (state->canonical_names == NULL) |
+ state->canonical_names = xcalloc (result.nelts, sizeof (char *)); |
+ make_cleanup (xfree, state->canonical_names); |
for (i = 0; i < result.nelts; ++i) |
{ |
- if (state.canonical_names[i] == NULL) |
- state.canonical_names[i] = savestring (arg_start, |
- *argptr - arg_start); |
- make_cleanup (xfree, state.canonical_names[i]); |
+ if (state->canonical_names[i] == NULL) |
+ state->canonical_names[i] = savestring (arg_start, |
+ *argptr - arg_start); |
+ make_cleanup (xfree, state->canonical_names[i]); |
} |
} |
@@ -1227,36 +2345,84 @@ decode_line_full (char **argptr, int flags, |
{ |
make_cleanup (VEC_cleanup (const_char_ptr), &filters); |
VEC_safe_push (const_char_ptr, filters, filter); |
- filter_results (&state, &result, filters); |
+ filter_results (state, &result, filters); |
} |
else |
- convert_results_to_lsals (&state, &result); |
+ convert_results_to_lsals (state, &result); |
} |
else |
- decode_line_2 (&state, &result, select_mode); |
+ decode_line_2 (state, &result, select_mode); |
do_cleanups (cleanups); |
} |
+/* See linespec.h. */ |
+ |
struct symtabs_and_lines |
decode_line_1 (char **argptr, int flags, |
struct symtab *default_symtab, |
int default_line) |
{ |
struct symtabs_and_lines result; |
- struct linespec_state state; |
+ linespec_parser parser; |
struct cleanup *cleanups; |
- linespec_state_constructor (&state, flags, |
- default_symtab, default_line, NULL); |
- cleanups = make_cleanup (linespec_state_destructor, &state); |
+ linespec_parser_new (&parser, flags, current_language, default_symtab, |
+ default_line, NULL); |
+ cleanups = make_cleanup (linespec_parser_delete, &parser); |
save_current_program_space (); |
- result = decode_line_internal (&state, argptr); |
+ result = parse_linespec (&parser, argptr); |
+ |
do_cleanups (cleanups); |
return result; |
} |
+/* See linespec.h. */ |
+ |
+struct symtabs_and_lines |
+decode_line_with_current_source (char *string, int flags) |
+{ |
+ struct symtabs_and_lines sals; |
+ struct symtab_and_line cursal; |
+ |
+ if (string == 0) |
+ error (_("Empty line specification.")); |
+ |
+ /* We use whatever is set as the current source line. We do not try |
+ and get a default source symtab+line or it will recursively call us! */ |
+ cursal = get_current_source_symtab_and_line (); |
+ |
+ sals = decode_line_1 (&string, flags, |
+ cursal.symtab, cursal.line); |
+ |
+ if (*string) |
+ error (_("Junk at end of line specification: %s"), string); |
+ return sals; |
+} |
+ |
+/* See linespec.h. */ |
+ |
+struct symtabs_and_lines |
+decode_line_with_last_displayed (char *string, int flags) |
+{ |
+ struct symtabs_and_lines sals; |
+ |
+ if (string == 0) |
+ error (_("Empty line specification.")); |
+ |
+ if (last_displayed_sal_is_valid ()) |
+ sals = decode_line_1 (&string, flags, |
+ get_last_displayed_symtab (), |
+ get_last_displayed_line ()); |
+ else |
+ sals = decode_line_1 (&string, flags, (struct symtab *) NULL, 0); |
+ |
+ if (*string) |
+ error (_("Junk at end of line specification: %s"), string); |
+ return sals; |
+} |
+ |
/* First, some functions to initialize stuff at the beggining of the |
@@ -1280,160 +2446,20 @@ initialize_defaults (struct symtab **default_symtab, int *default_line) |
-/* Decode arg of the form *PC. */ |
+/* Evaluate the expression pointed to by EXP_PTR into a CORE_ADDR, |
+ advancing EXP_PTR past any parsed text. */ |
-static struct symtabs_and_lines |
-decode_indirect (struct linespec_state *self, char **argptr) |
+static CORE_ADDR |
+linespec_expression_to_pc (char **exp_ptr) |
{ |
- struct symtabs_and_lines values; |
- CORE_ADDR pc; |
- char *initial = *argptr; |
- |
if (current_program_space->executing_startup) |
/* The error message doesn't really matter, because this case |
should only hit during breakpoint reset. */ |
throw_error (NOT_FOUND_ERROR, _("cannot evaluate expressions while " |
"program space is in startup")); |
- (*argptr)++; |
- pc = value_as_address (parse_to_comma_and_eval (argptr)); |
- |
- values.sals = (struct symtab_and_line *) |
- xmalloc (sizeof (struct symtab_and_line)); |
- |
- values.nelts = 1; |
- values.sals[0] = find_pc_line (pc, 0); |
- values.sals[0].pc = pc; |
- values.sals[0].section = find_pc_overlay (pc); |
- values.sals[0].explicit_pc = 1; |
- |
- if (self->canonical) |
- self->canonical->addr_string = savestring (initial, *argptr - initial); |
- |
- return values; |
-} |
- |
- |
- |
-/* Locate the first half of the linespec, ending in a colon, period, |
- or whitespace. (More or less.) Also, check to see if *ARGPTR is |
- enclosed in double quotes; if so, set is_quote_enclosed, advance |
- ARGPTR past that and zero out the trailing double quote. |
- If ARGPTR is just a simple name like "main", p will point to "" |
- at the end. */ |
- |
-static char * |
-locate_first_half (char **argptr, int *is_quote_enclosed) |
-{ |
- char *ii; |
- char *p, *p1; |
- int has_comma; |
- |
- /* Maybe we were called with a line range FILENAME:LINENUM,FILENAME:LINENUM |
- and we must isolate the first half. Outer layers will call again later |
- for the second half. |
- |
- Don't count commas that appear in argument lists of overloaded |
- functions, or in quoted strings. It's stupid to go to this much |
- trouble when the rest of the function is such an obvious roach hotel. */ |
- ii = find_toplevel_char (*argptr, ','); |
- has_comma = (ii != 0); |
- |
- /* Temporarily zap out second half to not confuse the code below. |
- This is undone below. Do not change ii!! */ |
- if (has_comma) |
- { |
- *ii = '\0'; |
- } |
- |
- /* Maybe arg is FILE : LINENUM or FILE : FUNCTION. May also be |
- CLASS::MEMBER, or NAMESPACE::NAME. Look for ':', but ignore |
- inside of <>. */ |
- |
- p = *argptr; |
- if (p[0] == '"') |
- { |
- *is_quote_enclosed = 1; |
- (*argptr)++; |
- p++; |
- } |
- else |
- { |
- *is_quote_enclosed = 0; |
- if (strchr (get_gdb_completer_quote_characters (), *p)) |
- { |
- ++(*argptr); |
- ++p; |
- } |
- } |
- |
- |
- /* Check for a drive letter in the filename. This is done on all hosts |
- to capture cross-compilation environments. On Unixen, directory |
- separators are illegal in filenames, so if the user enters "e:/foo.c", |
- he is referring to a directory named "e:" and a source file named |
- "foo.c", and we still want to keep these two pieces together. */ |
- if (isalpha (p[0]) && p[1] == ':' && IS_DIR_SEPARATOR (p[2])) |
- p += 3; |
- |
- for (; *p; p++) |
- { |
- if (p[0] == '<') |
- { |
- char *temp_end = find_template_name_end (p); |
- |
- if (!temp_end) |
- error (_("malformed template specification in command")); |
- p = temp_end; |
- } |
- |
- if (p[0] == '(') |
- p = find_method_overload_end (p); |
- |
- /* Check for a colon and a plus or minus and a [ (which |
- indicates an Objective-C method). */ |
- if (is_objc_method_format (p)) |
- { |
- break; |
- } |
- /* Check for the end of the first half of the linespec. End of |
- line, a tab, a colon or a space. But if enclosed in double |
- quotes we do not break on enclosed spaces. */ |
- if (!*p |
- || p[0] == '\t' |
- || (p[0] == ':') |
- || ((p[0] == ' ') && !*is_quote_enclosed)) |
- break; |
- if (p[0] == '.' && strchr (p, ':') == NULL) |
- { |
- /* Java qualified method. Find the *last* '.', since the |
- others are package qualifiers. Stop at any open parenthesis |
- which might provide overload information. */ |
- for (p1 = p; *p1 && *p1 != '('; p1++) |
- { |
- if (*p1 == '.') |
- p = p1; |
- } |
- break; |
- } |
- } |
- p = skip_spaces (p); |
- |
- /* If the closing double quote was left at the end, remove it. */ |
- if (*is_quote_enclosed) |
- { |
- char *closing_quote = strchr (p - 1, '"'); |
- |
- if (closing_quote && closing_quote[1] == '\0') |
- *closing_quote = '\0'; |
- } |
- |
- /* Now that we've safely parsed the first half, put back ',' so |
- outer layers can see it. */ |
- if (has_comma) |
- *ii = ','; |
- |
- return p; |
+ (*exp_ptr)++; |
+ return value_as_address (parse_to_comma_and_eval (exp_ptr)); |
} |
@@ -1446,28 +2472,35 @@ locate_first_half (char **argptr, int *is_quote_enclosed) |
the existing C++ code to let the user choose one. */ |
static struct symtabs_and_lines |
-decode_objc (struct linespec_state *self, char **argptr) |
+decode_objc (struct linespec_state *self, linespec_p ls, char **argptr) |
{ |
struct collect_info info; |
VEC (const_char_ptr) *symbol_names = NULL; |
+ struct symtabs_and_lines values; |
char *new_argptr; |
struct cleanup *cleanup = make_cleanup (VEC_cleanup (const_char_ptr), |
&symbol_names); |
info.state = self; |
- info.result.sals = NULL; |
- info.result.nelts = 0; |
+ info.file_symtabs = NULL; |
+ VEC_safe_push (symtab_p, info.file_symtabs, NULL); |
+ make_cleanup (VEC_cleanup (symtab_p), &info.file_symtabs); |
+ info.result.symbols = NULL; |
+ info.result.minimal_symbols = NULL; |
+ values.nelts = 0; |
+ values.sals = NULL; |
new_argptr = find_imps (*argptr, &symbol_names); |
if (VEC_empty (const_char_ptr, symbol_names)) |
{ |
do_cleanups (cleanup); |
- return info.result; |
+ return values; |
} |
add_all_symbol_names_from_pspace (&info, NULL, symbol_names); |
- if (info.result.nelts > 0) |
+ if (!VEC_empty (symbolp, info.result.symbols) |
+ || !VEC_empty (minsym_and_objfile_d, info.result.minimal_symbols)) |
{ |
char *saved_arg; |
@@ -1475,12 +2508,16 @@ decode_objc (struct linespec_state *self, char **argptr) |
memcpy (saved_arg, *argptr, new_argptr - *argptr); |
saved_arg[new_argptr - *argptr] = '\0'; |
+ ls->function_symbols = info.result.symbols; |
+ ls->minimal_symbols = info.result.minimal_symbols; |
+ values = convert_linespec_to_sals (self, ls); |
+ |
if (self->canonical) |
{ |
self->canonical->pre_expanded = 1; |
- if (self->user_filename) |
+ if (ls->source_filename) |
self->canonical->addr_string |
- = xstrprintf ("%s:%s", self->user_filename, saved_arg); |
+ = xstrprintf ("%s:%s", ls->source_filename, saved_arg); |
else |
self->canonical->addr_string = xstrdup (saved_arg); |
} |
@@ -1489,241 +2526,8 @@ decode_objc (struct linespec_state *self, char **argptr) |
*argptr = new_argptr; |
do_cleanups (cleanup); |
- return info.result; |
-} |
- |
-/* This handles C++ and Java compound data structures. P should point |
- at the first component separator, i.e. double-colon or period. As |
- an example, on entrance to this function we could have ARGPTR |
- pointing to "AAA::inA::fun" and P pointing to "::inA::fun". */ |
- |
-static struct symtabs_and_lines |
-decode_compound (struct linespec_state *self, |
- char **argptr, char *the_real_saved_arg, char *p) |
-{ |
- struct symtabs_and_lines values; |
- char *p2; |
- char *saved_arg2 = *argptr; |
- char *temp_end; |
- struct symbol *sym; |
- char *copy; |
- VEC (symbolp) *sym_classes; |
- char *saved_arg, *class_name; |
- struct cleanup *cleanup = make_cleanup (null_cleanup, NULL); |
- |
- /* If the user specified any completer quote characters in the input, |
- strip them. They are superfluous. */ |
- saved_arg = alloca (strlen (the_real_saved_arg) + 1); |
- { |
- char *dst = saved_arg; |
- char *src = the_real_saved_arg; |
- char *quotes = get_gdb_completer_quote_characters (); |
- while (*src != '\0') |
- { |
- if (strchr (quotes, *src) == NULL) |
- *dst++ = *src; |
- ++src; |
- } |
- *dst = '\0'; |
- } |
- |
- /* First check for "global" namespace specification, of the form |
- "::foo". If found, skip over the colons and jump to normal |
- symbol processing. I.e. the whole line specification starts with |
- "::" (note the condition that *argptr == p). */ |
- if (p[0] == ':' |
- && ((*argptr == p) || (p[-1] == ' ') || (p[-1] == '\t'))) |
- saved_arg2 += 2; |
- |
- /* Given our example "AAA::inA::fun", we have two cases to consider: |
- |
- 1) AAA::inA is the name of a class. In that case, presumably it |
- has a method called "fun"; we then look up that method using |
- find_method. |
- |
- 2) AAA::inA isn't the name of a class. In that case, either the |
- user made a typo, AAA::inA is the name of a namespace, or it is |
- the name of a minimal symbol. |
- In this case we just delegate to decode_variable. |
- |
- Thus, our first task is to find everything before the last set of |
- double-colons and figure out if it's the name of a class. So we |
- first loop through all of the double-colons. */ |
- |
- p2 = p; /* Save for restart. */ |
- |
- /* This is very messy. Following the example above we have now the |
- following pointers: |
- p -> "::inA::fun" |
- argptr -> "AAA::inA::fun |
- saved_arg -> "AAA::inA::fun |
- saved_arg2 -> "AAA::inA::fun |
- p2 -> "::inA::fun". */ |
- |
- /* In the loop below, with these strings, we'll make 2 passes, each |
- is marked in comments. */ |
- |
- while (1) |
- { |
- static char *break_characters = " \t("; |
- |
- /* Move pointer up to next possible class/namespace token. */ |
- |
- p = p2 + 1; /* Restart with old value +1. */ |
- |
- /* PASS1: at this point p2->"::inA::fun", so p->":inA::fun", |
- i.e. if there is a double-colon, p will now point to the |
- second colon. */ |
- /* PASS2: p2->"::fun", p->":fun" */ |
- /* Move pointer ahead to next double-colon. */ |
- while (*p |
- && strchr (break_characters, *p) == NULL |
- && strchr (get_gdb_completer_quote_characters (), *p) == NULL) |
- { |
- if (current_language->la_language == language_cplus) |
- p += cp_validate_operator (p); |
- |
- if (p[0] == '<') |
- { |
- temp_end = find_template_name_end (p); |
- if (!temp_end) |
- error (_("malformed template specification in command")); |
- p = temp_end; |
- } |
- /* Note that, since, at the start of this loop, p would be |
- pointing to the second colon in a double-colon, we only |
- satisfy the condition below if there is another |
- double-colon to the right (after). I.e. there is another |
- component that can be a class or a namespace. I.e, if at |
- the beginning of this loop (PASS1), we had |
- p->":inA::fun", we'll trigger this when p has been |
- advanced to point to "::fun". */ |
- /* PASS2: we will not trigger this. */ |
- else if ((p[0] == ':') && (p[1] == ':')) |
- break; /* Found double-colon. */ |
- else |
- { |
- /* PASS2: We'll keep getting here, until P points to one of the |
- break characters, at which point we exit this loop. */ |
- if (*p) |
- { |
- if (p[1] == '(' |
- && strncmp (&p[1], CP_ANONYMOUS_NAMESPACE_STR, |
- CP_ANONYMOUS_NAMESPACE_LEN) == 0) |
- p += CP_ANONYMOUS_NAMESPACE_LEN; |
- else if (strchr (break_characters, *p) == NULL) |
- ++p; |
- } |
- } |
- } |
- |
- if (*p != ':') |
- break; /* Out of the while (1). This would happen |
- for instance if we have looked up |
- unsuccessfully all the components of the |
- string, and p->""(PASS2). */ |
- |
- /* We get here if p points to one of the break characters or "" (i.e., |
- string ended). */ |
- /* Save restart for next time around. */ |
- p2 = p; |
- /* Restore argptr as it was on entry to this function. */ |
- *argptr = saved_arg2; |
- /* PASS1: at this point p->"::fun" argptr->"AAA::inA::fun", |
- p2->"::fun". */ |
- |
- /* All ready for next pass through the loop. */ |
- } /* while (1) */ |
- |
- |
- /* Start of lookup in the symbol tables. */ |
- |
- /* Lookup in the symbol table the substring between argptr and |
- p. Note, this call changes the value of argptr. */ |
- /* Before the call, argptr->"AAA::inA::fun", |
- p->"", p2->"::fun". After the call: argptr->"fun", p, p2 |
- unchanged. */ |
- sym_classes = lookup_prefix_sym (argptr, p2, self->file_symtabs, |
- &class_name); |
- make_cleanup (VEC_cleanup (symbolp), &sym_classes); |
- make_cleanup (xfree, class_name); |
- |
- /* If a class has been found, then we're in case 1 above. So we |
- look up "fun" as a method of those classes. */ |
- if (!VEC_empty (symbolp, sym_classes)) |
- { |
- /* Arg token is not digits => try it as a function name. |
- Find the next token (everything up to end or next |
- blank). */ |
- if (**argptr |
- && strchr (get_gdb_completer_quote_characters (), |
- **argptr) != NULL) |
- { |
- p = skip_quoted (*argptr); |
- *argptr = *argptr + 1; |
- } |
- else |
- { |
- /* At this point argptr->"fun". */ |
- char *a; |
- |
- p = *argptr; |
- while (*p && *p != ' ' && *p != '\t' && *p != ',' && *p != ':' |
- && *p != '(') |
- p++; |
- /* At this point p->"". String ended. */ |
- /* Nope, C++ operators could have spaces in them |
- ("foo::operator <" or "foo::operator delete []"). |
- I apologize, this is a bit hacky... */ |
- if (current_language->la_language == language_cplus |
- && *p == ' ' && p - 8 - *argptr + 1 > 0) |
- { |
- /* The above loop has already swallowed "operator". */ |
- p += cp_validate_operator (p - 8) - 8; |
- } |
- |
- /* Keep any important naming information. */ |
- p = keep_name_info (p, 1); |
- } |
- |
- /* Allocate our own copy of the substring between argptr and |
- p. */ |
- copy = (char *) alloca (p - *argptr + 1); |
- memcpy (copy, *argptr, p - *argptr); |
- copy[p - *argptr] = '\0'; |
- if (p != *argptr |
- && copy[p - *argptr - 1] |
- && strchr (get_gdb_completer_quote_characters (), |
- copy[p - *argptr - 1]) != NULL) |
- copy[p - *argptr - 1] = '\0'; |
- |
- /* At this point copy->"fun", p->"". */ |
- |
- /* No line number may be specified. */ |
- *argptr = skip_spaces (p); |
- /* At this point arptr->"". */ |
- |
- /* Look for copy as a method of sym_class. */ |
- /* At this point copy->"fun", sym_class is "AAA:inA", |
- saved_arg->"AAA::inA::fun". This concludes the scanning of |
- the string for possible components matches. If we find it |
- here, we return. If not, and we are at the and of the string, |
- we'll lookup the whole string in the symbol tables. */ |
- |
- values = find_method (self, saved_arg, copy, class_name, sym_classes); |
- |
- do_cleanups (cleanup); |
- return values; |
- } /* End if symbol found. */ |
- |
- |
- /* We couldn't find a class, so we're in case 2 above. We check the |
- entire name as a symbol instead. The simplest way to do this is |
- to just throw an exception and let our caller fall through to |
- decode_variable. */ |
- |
- throw_error (NOT_FOUND_ERROR, _("see caller, this text doesn't matter")); |
+ return values; |
} |
/* An instance of this type is used when collecting prefix symbols for |
@@ -1750,14 +2554,14 @@ collect_one_symbol (struct symbol *sym, void *d) |
struct type *t; |
if (SYMBOL_CLASS (sym) != LOC_TYPEDEF) |
- return 1; |
+ return 1; /* Continue iterating. */ |
t = SYMBOL_TYPE (sym); |
CHECK_TYPEDEF (t); |
if (TYPE_CODE (t) != TYPE_CODE_STRUCT |
&& TYPE_CODE (t) != TYPE_CODE_UNION |
&& TYPE_CODE (t) != TYPE_CODE_NAMESPACE) |
- return 1; |
+ return 1; /* Continue iterating. */ |
slot = htab_find_slot (collector->unique_syms, sym, INSERT); |
if (!*slot) |
@@ -1766,49 +2570,23 @@ collect_one_symbol (struct symbol *sym, void *d) |
VEC_safe_push (symbolp, collector->symbols, sym); |
} |
- return 1; |
+ return 1; /* Continue iterating. */ |
} |
-/* Return the symbol corresponding to the substring of *ARGPTR ending |
- at P, allowing whitespace. Also, advance *ARGPTR past the symbol |
- name in question, the compound object separator ("::" or "."), and |
- whitespace. Note that *ARGPTR is changed whether or not the |
- this call finds anything (i.e we return NULL). As an |
- example, say ARGPTR is "AAA::inA::fun" and P is "::inA::fun". */ |
+/* Return any symbols corresponding to CLASS_NAME in FILE_SYMTABS. */ |
static VEC (symbolp) * |
-lookup_prefix_sym (char **argptr, char *p, VEC (symtab_p) *file_symtabs, |
- char **class_name) |
+lookup_prefix_sym (struct linespec_state *state, VEC (symtab_p) *file_symtabs, |
+ const char *class_name) |
{ |
- char *p1; |
- char *copy; |
int ix; |
struct symtab *elt; |
struct decode_compound_collector collector; |
struct cleanup *outer; |
struct cleanup *cleanup; |
- struct block *search_block; |
- |
- /* Extract the class name. */ |
- p1 = p; |
- while (p != *argptr && p[-1] == ' ') |
- --p; |
- copy = (char *) xmalloc (p - *argptr + 1); |
- memcpy (copy, *argptr, p - *argptr); |
- copy[p - *argptr] = 0; |
- *class_name = copy; |
- outer = make_cleanup (xfree, copy); |
- |
- /* Discard the class name from the argptr. */ |
- p = p1 + (p1[0] == ':' ? 2 : 1); |
- p = skip_spaces (p); |
- *argptr = p; |
- |
- /* At this point p1->"::inA::fun", p->"inA::fun" copy->"AAA", |
- argptr->"inA::fun". */ |
collector.symbols = NULL; |
- make_cleanup (VEC_cleanup (symbolp), &collector.symbols); |
+ outer = make_cleanup (VEC_cleanup (symbolp), &collector.symbols); |
collector.unique_syms = htab_create_alloc (1, htab_hash_pointer, |
htab_eq_pointer, NULL, |
@@ -1819,12 +2597,12 @@ lookup_prefix_sym (char **argptr, char *p, VEC (symtab_p) *file_symtabs, |
{ |
if (elt == NULL) |
{ |
- iterate_over_all_matching_symtabs (copy, STRUCT_DOMAIN, |
+ iterate_over_all_matching_symtabs (state, class_name, STRUCT_DOMAIN, |
collect_one_symbol, &collector, |
- NULL); |
- iterate_over_all_matching_symtabs (copy, VAR_DOMAIN, |
+ NULL, 0); |
+ iterate_over_all_matching_symtabs (state, class_name, VAR_DOMAIN, |
collect_one_symbol, &collector, |
- NULL); |
+ NULL, 0); |
} |
else |
{ |
@@ -1835,9 +2613,9 @@ lookup_prefix_sym (char **argptr, char *p, VEC (symtab_p) *file_symtabs, |
gdb_assert (!SYMTAB_PSPACE (elt)->executing_startup); |
set_current_program_space (SYMTAB_PSPACE (elt)); |
search_block = get_search_block (elt); |
- LA_ITERATE_OVER_SYMBOLS (search_block, copy, STRUCT_DOMAIN, |
+ LA_ITERATE_OVER_SYMBOLS (search_block, class_name, STRUCT_DOMAIN, |
collect_one_symbol, &collector); |
- LA_ITERATE_OVER_SYMBOLS (search_block, copy, VAR_DOMAIN, |
+ LA_ITERATE_OVER_SYMBOLS (search_block, class_name, VAR_DOMAIN, |
collect_one_symbol, &collector); |
} |
} |
@@ -1847,27 +2625,55 @@ lookup_prefix_sym (char **argptr, char *p, VEC (symtab_p) *file_symtabs, |
return collector.symbols; |
} |
-/* A qsort comparison function for symbols. The resulting order does |
- not actually matter; we just need to be able to sort them so that |
- symbols with the same program space end up next to each other. */ |
+/* A qsort comparison function for symbols. The resulting order does |
+ not actually matter; we just need to be able to sort them so that |
+ symbols with the same program space end up next to each other. */ |
+ |
+static int |
+compare_symbols (const void *a, const void *b) |
+{ |
+ struct symbol * const *sa = a; |
+ struct symbol * const *sb = b; |
+ uintptr_t uia, uib; |
+ |
+ uia = (uintptr_t) SYMTAB_PSPACE (SYMBOL_SYMTAB (*sa)); |
+ uib = (uintptr_t) SYMTAB_PSPACE (SYMBOL_SYMTAB (*sb)); |
+ |
+ if (uia < uib) |
+ return -1; |
+ if (uia > uib) |
+ return 1; |
+ |
+ uia = (uintptr_t) *sa; |
+ uib = (uintptr_t) *sb; |
+ |
+ if (uia < uib) |
+ return -1; |
+ if (uia > uib) |
+ return 1; |
+ |
+ return 0; |
+} |
+ |
+/* Like compare_symbols but for minimal symbols. */ |
static int |
-compare_symbols (const void *a, const void *b) |
+compare_msymbols (const void *a, const void *b) |
{ |
- struct symbol * const *sa = a; |
- struct symbol * const *sb = b; |
+ const struct minsym_and_objfile *sa = a; |
+ const struct minsym_and_objfile *sb = b; |
uintptr_t uia, uib; |
- uia = (uintptr_t) SYMTAB_PSPACE (SYMBOL_SYMTAB (*sa)); |
- uib = (uintptr_t) SYMTAB_PSPACE (SYMBOL_SYMTAB (*sb)); |
+ uia = (uintptr_t) sa->objfile->pspace; |
+ uib = (uintptr_t) sa->objfile->pspace; |
if (uia < uib) |
return -1; |
if (uia > uib) |
return 1; |
- uia = (uintptr_t) *sa; |
- uib = (uintptr_t) *sb; |
+ uia = (uintptr_t) sa->minsym; |
+ uib = (uintptr_t) sb->minsym; |
if (uia < uib) |
return -1; |
@@ -1924,14 +2730,16 @@ find_superclass_methods (VEC (typep) *superclasses, |
do_cleanups (cleanup); |
} |
-/* This finds the method COPY in the class whose type is given by one |
- of the symbols in SYM_CLASSES. */ |
+/* This finds the method METHOD_NAME in the class CLASS_NAME whose type is |
+ given by one of the symbols in SYM_CLASSES. Matches are returned |
+ in SYMBOLS (for debug symbols) and MINSYMS (for minimal symbols). */ |
-static struct symtabs_and_lines |
-find_method (struct linespec_state *self, char *saved_arg, |
- char *copy, const char *class_name, VEC (symbolp) *sym_classes) |
+static void |
+find_method (struct linespec_state *self, VEC (symtab_p) *file_symtabs, |
+ const char *class_name, const char *method_name, |
+ VEC (symbolp) *sym_classes, VEC (symbolp) **symbols, |
+ VEC (minsym_and_objfile_d) **minsyms) |
{ |
- char *canon; |
struct symbol *sym; |
struct cleanup *cleanup = make_cleanup (null_cleanup, NULL); |
int ix; |
@@ -1939,16 +2747,6 @@ find_method (struct linespec_state *self, char *saved_arg, |
VEC (typep) *superclass_vec; |
VEC (const_char_ptr) *result_names; |
struct collect_info info; |
- char *name_iter; |
- |
- /* NAME is typed by the user: it needs to be canonicalized before |
- searching the symbol tables. */ |
- canon = cp_canonicalize_string_no_typedefs (copy); |
- if (canon != NULL) |
- { |
- copy = canon; |
- make_cleanup (xfree, copy); |
- } |
/* Sort symbols so that symbols with the same program space are next |
to each other. */ |
@@ -1958,11 +2756,12 @@ find_method (struct linespec_state *self, char *saved_arg, |
compare_symbols); |
info.state = self; |
- info.result.sals = NULL; |
- info.result.nelts = 0; |
+ info.file_symtabs = file_symtabs; |
+ info.result.symbols = NULL; |
+ info.result.minimal_symbols = NULL; |
/* Iterate over all the types, looking for the names of existing |
- methods matching COPY. If we cannot find a direct method in a |
+ methods matching METHOD_NAME. If we cannot find a direct method in a |
given program space, then we consider inherited methods; this is |
not ideal (ideal would be to respect C++ hiding rules), but it |
seems good enough and is what GDB has historically done. We only |
@@ -1986,7 +2785,7 @@ find_method (struct linespec_state *self, char *saved_arg, |
pspace = SYMTAB_PSPACE (SYMBOL_SYMTAB (sym)); |
set_current_program_space (pspace); |
t = check_typedef (SYMBOL_TYPE (sym)); |
- find_methods (t, copy, &result_names, &superclass_vec); |
+ find_methods (t, method_name, &result_names, &superclass_vec); |
/* Handle all items from a single program space at once; and be |
sure not to miss the last batch. */ |
@@ -1998,7 +2797,8 @@ find_method (struct linespec_state *self, char *saved_arg, |
/* If we did not find a direct implementation anywhere in |
this program space, consider superclasses. */ |
if (VEC_length (const_char_ptr, result_names) == last_result_len) |
- find_superclass_methods (superclass_vec, copy, &result_names); |
+ find_superclass_methods (superclass_vec, method_name, |
+ &result_names); |
/* We have a list of candidate symbol names, so now we |
iterate over the symbol tables looking for all |
@@ -2010,31 +2810,18 @@ find_method (struct linespec_state *self, char *saved_arg, |
} |
} |
- if (info.result.nelts > 0) |
+ if (!VEC_empty (symbolp, info.result.symbols) |
+ || !VEC_empty (minsym_and_objfile_d, info.result.minimal_symbols)) |
{ |
- if (self->canonical) |
- { |
- self->canonical->pre_expanded = 1; |
- if (self->user_filename) |
- self->canonical->addr_string |
- = xstrprintf ("%s:%s", self->user_filename, saved_arg); |
- else |
- self->canonical->addr_string = xstrdup (saved_arg); |
- } |
- |
+ *symbols = info.result.symbols; |
+ *minsyms = info.result.minimal_symbols; |
do_cleanups (cleanup); |
- |
- return info.result; |
+ return; |
} |
- if (copy[0] == '~') |
- cplusplus_error (saved_arg, |
- "the class `%s' does not have destructor defined\n", |
- class_name); |
- else |
- cplusplus_error (saved_arg, |
- "the class %s does not have any method named %s\n", |
- class_name, copy); |
+ /* Throw an NOT_FOUND_ERROR. This will be caught by the caller |
+ and other attempts to locate the symbol will be made. */ |
+ throw_error (NOT_FOUND_ERROR, _("see caller, this text doesn't matter")); |
} |
@@ -2096,487 +2883,193 @@ collect_symtabs_from_filename (const char *file) |
return collector.symtabs; |
} |
-/* Return all the symtabs associated to the filename given by the |
- substring of *ARGPTR ending at P, and advance ARGPTR past that |
- filename. */ |
+/* Return all the symtabs associated to the FILENAME. */ |
static VEC (symtab_p) * |
-symtabs_from_filename (char **argptr, char *p, int is_quote_enclosed, |
- char **user_filename) |
-{ |
- char *p1; |
- char *copy; |
- struct cleanup *outer; |
- VEC (symtab_p) *result; |
- |
- p1 = p; |
- while (p != *argptr && p[-1] == ' ') |
- --p; |
- if ((*p == '"') && is_quote_enclosed) |
- --p; |
- copy = xmalloc (p - *argptr + 1); |
- outer = make_cleanup (xfree, copy); |
- memcpy (copy, *argptr, p - *argptr); |
- /* It may have the ending quote right after the file name. */ |
- if ((is_quote_enclosed && copy[p - *argptr - 1] == '"') |
- || copy[p - *argptr - 1] == '\'') |
- copy[p - *argptr - 1] = 0; |
- else |
- copy[p - *argptr] = 0; |
- |
- result = collect_symtabs_from_filename (copy); |
- |
- if (VEC_empty (symtab_p, result)) |
- { |
- if (!have_full_symbols () && !have_partial_symbols ()) |
- throw_error (NOT_FOUND_ERROR, |
- _("No symbol table is loaded. " |
- "Use the \"file\" command.")); |
- throw_error (NOT_FOUND_ERROR, _("No source file named %s."), copy); |
- } |
- |
- /* Discard the file name from the arg. */ |
- if (*p1 == '\0') |
- *argptr = p1; |
- else |
- *argptr = skip_spaces (p1 + 1); |
- |
- discard_cleanups (outer); |
- *user_filename = copy; |
- return result; |
-} |
- |
-/* A callback used by iterate_over_all_matching_symtabs that collects |
- symbols for find_function_symbols. */ |
- |
-static int |
-collect_function_symbols (struct symbol *sym, void *arg) |
-{ |
- VEC (symbolp) **syms = arg; |
- |
- if (SYMBOL_CLASS (sym) == LOC_BLOCK) |
- VEC_safe_push (symbolp, *syms, sym); |
- |
- return 1; |
-} |
- |
-/* Look up a function symbol in *ARGPTR. If found, advance *ARGPTR |
- and return the symbol. If not found, return NULL. */ |
- |
-static VEC (symbolp) * |
-find_function_symbols (char **argptr, char *p, int is_quote_enclosed, |
- char **user_function) |
-{ |
- char *p1; |
- char *copy; |
- VEC (symbolp) *result = NULL; |
- |
- p1 = p; |
- while (p != *argptr && p[-1] == ' ') |
- --p; |
- if ((*p == '"') && is_quote_enclosed) |
- --p; |
- copy = (char *) xmalloc (p - *argptr + 1); |
- *user_function = copy; |
- memcpy (copy, *argptr, p - *argptr); |
- /* It may have the ending quote right after the file name. */ |
- if ((is_quote_enclosed && copy[p - *argptr - 1] == '"') |
- || copy[p - *argptr - 1] == '\'') |
- copy[p - *argptr - 1] = 0; |
- else |
- copy[p - *argptr] = 0; |
- |
- iterate_over_all_matching_symtabs (copy, VAR_DOMAIN, |
- collect_function_symbols, &result, NULL); |
- |
- if (VEC_empty (symbolp, result)) |
- VEC_free (symbolp, result); |
- else |
- { |
- /* Discard the file name from the arg. */ |
- *argptr = skip_spaces (p1 + 1); |
- } |
- |
- return result; |
-} |
- |
- |
- |
-/* A helper for decode_all_digits that handles the 'list_mode' case. */ |
- |
-static void |
-decode_digits_list_mode (struct linespec_state *self, |
- struct symtabs_and_lines *values, |
- struct symtab_and_line val) |
-{ |
- int ix; |
- struct symtab *elt; |
- |
- gdb_assert (self->list_mode); |
- |
- for (ix = 0; VEC_iterate (symtab_p, self->file_symtabs, ix, elt); ++ix) |
- { |
- /* The logic above should ensure this. */ |
- gdb_assert (elt != NULL); |
- |
- set_current_program_space (SYMTAB_PSPACE (elt)); |
- |
- /* Simplistic search just for the list command. */ |
- val.symtab = find_line_symtab (elt, val.line, NULL, NULL); |
- if (val.symtab == NULL) |
- val.symtab = elt; |
- val.pspace = SYMTAB_PSPACE (elt); |
- val.pc = 0; |
- val.explicit_line = 1; |
- |
- add_sal_to_sals (self, values, &val, NULL); |
- } |
-} |
- |
-/* A helper for decode_all_digits that iterates over the symtabs, |
- adding lines to the VEC. */ |
- |
-static void |
-decode_digits_ordinary (struct linespec_state *self, |
- int line, |
- struct symtabs_and_lines *sals, |
- struct linetable_entry **best_entry) |
-{ |
- int ix; |
- struct symtab *elt; |
- |
- for (ix = 0; VEC_iterate (symtab_p, self->file_symtabs, ix, elt); ++ix) |
- { |
- int i; |
- VEC (CORE_ADDR) *pcs; |
- CORE_ADDR pc; |
- |
- /* The logic above should ensure this. */ |
- gdb_assert (elt != NULL); |
- |
- set_current_program_space (SYMTAB_PSPACE (elt)); |
- |
- pcs = find_pcs_for_symtab_line (elt, line, best_entry); |
- for (i = 0; VEC_iterate (CORE_ADDR, pcs, i, pc); ++i) |
- { |
- struct symtab_and_line sal; |
- |
- init_sal (&sal); |
- sal.pspace = SYMTAB_PSPACE (elt); |
- sal.symtab = elt; |
- sal.line = line; |
- sal.pc = pc; |
- add_sal_to_sals_basic (sals, &sal); |
- } |
- |
- VEC_free (CORE_ADDR, pcs); |
- } |
-} |
- |
-/* This decodes a line where the argument is all digits (possibly |
- preceded by a sign). Q should point to the end of those digits; |
- the other arguments are as usual. */ |
- |
-static struct symtabs_and_lines |
-decode_all_digits (struct linespec_state *self, |
- char **argptr, |
- char *q) |
+symtabs_from_filename (const char *filename) |
{ |
- struct symtabs_and_lines values; |
- struct symtab_and_line val; |
- int use_default = 0; |
- char *saved_arg = *argptr; |
- |
- enum sign |
- { |
- none, plus, minus |
- } |
- sign = none; |
- |
- init_sal (&val); |
- values.sals = NULL; |
- values.nelts = 0; |
- |
- /* This is where we need to make sure that we have good defaults. |
- We must guarantee that this section of code is never executed |
- when we are called with just a function name, since |
- set_default_source_symtab_and_line uses |
- select_source_symtab that calls us with such an argument. */ |
- |
- if (VEC_length (symtab_p, self->file_symtabs) == 1 |
- && VEC_index (symtab_p, self->file_symtabs, 0) == NULL) |
- { |
- set_current_program_space (self->program_space); |
- |
- /* Make sure we have at least a default source file. */ |
- set_default_source_symtab_and_line (); |
- initialize_defaults (&self->default_symtab, &self->default_line); |
- VEC_pop (symtab_p, self->file_symtabs); |
- VEC_free (symtab_p, self->file_symtabs); |
- self->file_symtabs |
- = collect_symtabs_from_filename (self->default_symtab->filename); |
- use_default = 1; |
- } |
- |
- if (**argptr == '+') |
- sign = plus, (*argptr)++; |
- else if (**argptr == '-') |
- sign = minus, (*argptr)++; |
- val.line = atoi (*argptr); |
- switch (sign) |
- { |
- case plus: |
- if (q == *argptr) |
- val.line = 5; |
- if (use_default) |
- val.line = self->default_line + val.line; |
- break; |
- case minus: |
- if (q == *argptr) |
- val.line = 15; |
- if (use_default) |
- val.line = self->default_line - val.line; |
- else |
- val.line = 1; |
- break; |
- case none: |
- break; /* No need to adjust val.line. */ |
- } |
- |
- *argptr = skip_spaces (q); |
- |
- if (self->list_mode) |
- decode_digits_list_mode (self, &values, val); |
- else |
- { |
- struct linetable_entry *best_entry = NULL; |
- int *filter; |
- struct block **blocks; |
- struct cleanup *cleanup; |
- struct symtabs_and_lines intermediate_results; |
- int i, j; |
- |
- intermediate_results.sals = NULL; |
- intermediate_results.nelts = 0; |
- |
- decode_digits_ordinary (self, val.line, &intermediate_results, |
- &best_entry); |
- if (intermediate_results.nelts == 0 && best_entry != NULL) |
- decode_digits_ordinary (self, best_entry->line, &intermediate_results, |
- &best_entry); |
- |
- cleanup = make_cleanup (xfree, intermediate_results.sals); |
- |
- /* For optimized code, compiler can scatter one source line |
- accross disjoint ranges of PC values, even when no duplicate |
- functions or inline functions are involved. For example, |
- 'for (;;)' inside non-template non-inline non-ctor-or-dtor |
- function can result in two PC ranges. In this case, we don't |
- want to set breakpoint on first PC of each range. To filter |
- such cases, we use containing blocks -- for each PC found |
- above we see if there are other PCs that are in the same |
- block. If yes, the other PCs are filtered out. */ |
- |
- filter = xmalloc (intermediate_results.nelts * sizeof (int)); |
- make_cleanup (xfree, filter); |
- blocks = xmalloc (intermediate_results.nelts * sizeof (struct block *)); |
- make_cleanup (xfree, blocks); |
- |
- for (i = 0; i < intermediate_results.nelts; ++i) |
- { |
- set_current_program_space (intermediate_results.sals[i].pspace); |
- |
- filter[i] = 1; |
- blocks[i] = block_for_pc_sect (intermediate_results.sals[i].pc, |
- intermediate_results.sals[i].section); |
- } |
- |
- for (i = 0; i < intermediate_results.nelts; ++i) |
- { |
- if (blocks[i] != NULL) |
- for (j = i + 1; j < intermediate_results.nelts; ++j) |
- { |
- if (blocks[j] == blocks[i]) |
- { |
- filter[j] = 0; |
- break; |
- } |
- } |
- } |
- |
- for (i = 0; i < intermediate_results.nelts; ++i) |
- if (filter[i]) |
- { |
- struct symbol *sym = (blocks[i] |
- ? block_containing_function (blocks[i]) |
- : NULL); |
- |
- if (self->funfirstline) |
- skip_prologue_sal (&intermediate_results.sals[i]); |
- /* Make sure the line matches the request, not what was |
- found. */ |
- intermediate_results.sals[i].line = val.line; |
- add_sal_to_sals (self, &values, &intermediate_results.sals[i], |
- sym ? SYMBOL_NATURAL_NAME (sym) : NULL); |
- } |
- |
- do_cleanups (cleanup); |
- } |
- |
- if (values.nelts == 0) |
- { |
- if (self->user_filename) |
- throw_error (NOT_FOUND_ERROR, _("No line %d in file \"%s\"."), |
- val.line, self->user_filename); |
- else |
- throw_error (NOT_FOUND_ERROR, _("No line %d in the current file."), |
- val.line); |
- } |
+ VEC (symtab_p) *result; |
+ |
+ result = collect_symtabs_from_filename (filename); |
- if (self->canonical) |
+ if (VEC_empty (symtab_p, result)) |
{ |
- char *copy = savestring (saved_arg, q - saved_arg); |
- |
- self->canonical->pre_expanded = 1; |
- gdb_assert (self->user_filename || use_default); |
- self->canonical->addr_string |
- = xstrprintf ("%s:%s", (self->user_filename |
- ? self->user_filename |
- : self->default_symtab->filename), |
- copy); |
- xfree (copy); |
+ if (!have_full_symbols () && !have_partial_symbols ()) |
+ throw_error (NOT_FOUND_ERROR, |
+ _("No symbol table is loaded. " |
+ "Use the \"file\" command.")); |
+ throw_error (NOT_FOUND_ERROR, _("No source file named %s."), filename); |
} |
- return values; |
+ return result; |
} |
- |
- |
-/* Decode a linespec starting with a dollar sign. */ |
+/* Look up a function symbol named NAME in symtabs FILE_SYMTABS. Matching |
+ debug symbols are returned in SYMBOLS. Matching minimal symbols are |
+ returned in MINSYMS. */ |
-static struct symtabs_and_lines |
-decode_dollar (struct linespec_state *self, char *copy) |
+static void |
+find_function_symbols (struct linespec_state *state, |
+ VEC (symtab_p) *file_symtabs, const char *name, |
+ VEC (symbolp) **symbols, |
+ VEC (minsym_and_objfile_d) **minsyms) |
{ |
- LONGEST valx; |
- int index = 0; |
- struct symtabs_and_lines values; |
- struct symtab_and_line val; |
- char *p; |
- struct symbol *sym; |
- struct minimal_symbol *msymbol; |
- int ix; |
- struct symtab *elt; |
+ struct collect_info info; |
+ VEC (const_char_ptr) *symbol_names = NULL; |
+ struct cleanup *cleanup = make_cleanup (VEC_cleanup (const_char_ptr), |
+ &symbol_names); |
- p = (copy[1] == '$') ? copy + 2 : copy + 1; |
- while (*p >= '0' && *p <= '9') |
- p++; |
- if (!*p) /* Reached end of token without hitting non-digit. */ |
- { |
- /* We have a value history reference. */ |
- struct value *val_history; |
+ info.state = state; |
+ info.result.symbols = NULL; |
+ info.result.minimal_symbols = NULL; |
+ info.file_symtabs = file_symtabs; |
- sscanf ((copy[1] == '$') ? copy + 2 : copy + 1, "%d", &index); |
- val_history = access_value_history ((copy[1] == '$') ? -index : index); |
- if (TYPE_CODE (value_type (val_history)) != TYPE_CODE_INT) |
- error (_("History values used in line " |
- "specs must have integer values.")); |
- valx = value_as_long (val_history); |
+ /* Try NAME as an Objective-C selector. */ |
+ find_imps ((char *) name, &symbol_names); |
+ if (!VEC_empty (const_char_ptr, symbol_names)) |
+ add_all_symbol_names_from_pspace (&info, NULL, symbol_names); |
+ else |
+ add_matching_symbols_to_info (name, &info, NULL); |
+ |
+ do_cleanups (cleanup); |
+ |
+ if (VEC_empty (symbolp, info.result.symbols)) |
+ { |
+ VEC_free (symbolp, info.result.symbols); |
+ *symbols = NULL; |
} |
else |
+ *symbols = info.result.symbols; |
+ |
+ if (VEC_empty (minsym_and_objfile_d, info.result.minimal_symbols)) |
{ |
- /* Not all digits -- may be user variable/function or a |
- convenience variable. */ |
+ VEC_free (minsym_and_objfile_d, info.result.minimal_symbols); |
+ *minsyms = NULL; |
+ } |
+ else |
+ *minsyms = info.result.minimal_symbols; |
+} |
- volatile struct gdb_exception exc; |
+/* Find all symbols named NAME in FILE_SYMTABS, returning debug symbols |
+ in SYMBOLS and minimal symbols in MINSYMS. */ |
- /* Avoid "may be used uninitialized" warning. */ |
- values.sals = NULL; |
- values.nelts = 0; |
+void |
+find_linespec_symbols (struct linespec_state *state, |
+ VEC (symtab_p) *file_symtabs, |
+ const char *name, |
+ VEC (symbolp) **symbols, |
+ VEC (minsym_and_objfile_d) **minsyms) |
+{ |
+ char *klass, *method, *canon; |
+ const char *lookup_name, *last, *p, *scope_op; |
+ struct cleanup *cleanup; |
+ VEC (symbolp) *classes; |
+ volatile struct gdb_exception except; |
- TRY_CATCH (exc, RETURN_MASK_ERROR) |
- { |
- values = decode_variable (self, copy); |
- } |
+ cleanup = demangle_for_lookup (name, state->language->la_language, |
+ &lookup_name); |
+ if (state->language->la_language == language_ada) |
+ { |
+ /* In Ada, the symbol lookups are performed using the encoded |
+ name rather than the demangled name. */ |
+ lookup_name = ada_name_for_lookup (name); |
+ make_cleanup (xfree, (void *) lookup_name); |
+ } |
- if (exc.reason == 0) |
- return values; |
+ canon = cp_canonicalize_string_no_typedefs (lookup_name); |
+ if (canon != NULL) |
+ { |
+ lookup_name = canon; |
+ cleanup = make_cleanup (xfree, canon); |
+ } |
- if (exc.error != NOT_FOUND_ERROR) |
- throw_exception (exc); |
+ /* See if we can find a scope operator and break this symbol |
+ name into namespaces${SCOPE_OPERATOR}class_name and method_name. */ |
+ scope_op = "::"; |
+ p = find_toplevel_string (lookup_name, scope_op); |
+ if (p == NULL) |
+ { |
+ /* No C++ scope operator. Try Java. */ |
+ scope_op = "."; |
+ p = find_toplevel_string (lookup_name, scope_op); |
+ } |
- /* Not a user variable or function -- must be convenience variable. */ |
- if (!get_internalvar_integer (lookup_internalvar (copy + 1), &valx)) |
- error (_("Convenience variables used in line " |
- "specs must have integer values.")); |
+ last = NULL; |
+ while (p != NULL) |
+ { |
+ last = p; |
+ p = find_toplevel_string (p + strlen (scope_op), scope_op); |
} |
- init_sal (&val); |
+ /* If no scope operator was found, lookup the name as a symbol. */ |
+ if (last == NULL) |
+ { |
+ find_function_symbols (state, file_symtabs, lookup_name, |
+ symbols, minsyms); |
+ do_cleanups (cleanup); |
+ return; |
+ } |
- values.sals = NULL; |
- values.nelts = 0; |
+ /* NAME points to the class name. |
+ LAST points to the method name. */ |
+ klass = xmalloc ((last - lookup_name + 1) * sizeof (char)); |
+ make_cleanup (xfree, klass); |
+ strncpy (klass, lookup_name, last - lookup_name); |
+ klass[last - lookup_name] = '\0'; |
+ |
+ /* Skip past the scope operator. */ |
+ last += strlen (scope_op); |
+ method = xmalloc ((strlen (last) + 1) * sizeof (char)); |
+ make_cleanup (xfree, method); |
+ strcpy (method, last); |
- for (ix = 0; VEC_iterate (symtab_p, self->file_symtabs, ix, elt); ++ix) |
+ /* Find a list of classes named KLASS. */ |
+ classes = lookup_prefix_sym (state, file_symtabs, klass); |
+ make_cleanup (VEC_cleanup (symbolp), &classes); |
+ if (!VEC_empty (symbolp, classes)) |
{ |
- if (elt == NULL) |
+ /* Now locate a list of suitable methods named METHOD. */ |
+ TRY_CATCH (except, RETURN_MASK_ERROR) |
{ |
- elt = self->default_symtab; |
- set_current_program_space (self->program_space); |
+ find_method (state, file_symtabs, klass, method, classes, |
+ symbols, minsyms); |
} |
- else |
- set_current_program_space (SYMTAB_PSPACE (elt)); |
- |
- /* Either history value or convenience value from above, in valx. */ |
- val.symtab = elt; |
- val.line = valx; |
- val.pc = 0; |
- val.pspace = elt ? SYMTAB_PSPACE (elt) : current_program_space; |
- add_sal_to_sals (self, &values, &val, NULL); |
- } |
- |
- if (self->canonical) |
- { |
- self->canonical->pre_expanded = 1; |
- if (self->user_filename) |
- self->canonical->addr_string = xstrprintf ("%s:%s", |
- self->user_filename, copy); |
- else |
- self->canonical->addr_string = xstrdup (copy); |
+ /* If successful, we're done. If NOT_FOUND_ERROR |
+ was not thrown, rethrow the exception that we did get. |
+ Otherwise, fall back to looking up the entire name as a symbol. |
+ This can happen with namespace::function. */ |
+ if (except.reason >= 0) |
+ { |
+ do_cleanups (cleanup); |
+ return; |
+ } |
+ else if (except.error != NOT_FOUND_ERROR) |
+ throw_exception (except); |
} |
- return values; |
+ /* We couldn't find a class, so we check the entire name as a symbol |
+ instead. */ |
+ find_function_symbols (state, file_symtabs, lookup_name, symbols, minsyms); |
+ do_cleanups (cleanup); |
} |
- |
- |
-/* A helper for decode_line_1 that tries to find a label. The label |
- is searched for in the current block. |
- FUNCTION_SYMBOLS is a list of the enclosing functions; or NULL if none |
- specified. |
- COPY is the name of the label to find. |
- CANONICAL is the same as the "canonical" argument to decode_line_1. |
- RESULT is a pointer to a symtabs_and_lines structure which will be |
- filled in on success. |
- This function returns 1 if a label was found, 0 otherwise. */ |
+/* Return all labels named NAME in FUNCTION_SYMBOLS. Return the |
+ actual function symbol in which the label was found in LABEL_FUNC_RET. */ |
-static int |
-decode_label (struct linespec_state *self, |
- VEC (symbolp) *function_symbols, char *copy, |
- struct symtabs_and_lines *result) |
+static VEC (symbolp) * |
+find_label_symbols (struct linespec_state *self, |
+ VEC (symbolp) *function_symbols, |
+ VEC (symbolp) **label_funcs_ret, const char *name) |
{ |
- struct symbol *fn_sym; |
int ix; |
+ struct block *block; |
+ struct symbol *sym; |
+ struct symbol *fn_sym; |
+ VEC (symbolp) *result = NULL; |
if (function_symbols == NULL) |
{ |
- struct block *block; |
- struct symbol *sym; |
- struct symtab_and_line sal; |
- struct symtabs_and_lines values; |
- |
- values.nelts = 0; |
- values.sals = NULL; |
- |
set_current_program_space (self->program_space); |
block = get_search_block (NULL); |
@@ -2585,69 +3078,171 @@ decode_label (struct linespec_state *self, |
block = BLOCK_SUPERBLOCK (block)) |
; |
if (!block) |
- return 0; |
+ return NULL; |
fn_sym = BLOCK_FUNCTION (block); |
- sym = lookup_symbol (copy, block, LABEL_DOMAIN, 0); |
+ sym = lookup_symbol (name, block, LABEL_DOMAIN, 0); |
- if (sym == NULL) |
- return 0; |
- |
- symbol_to_sal (&sal, self->funfirstline, sym); |
- add_sal_to_sals (self, &values, &sal, |
- SYMBOL_NATURAL_NAME (fn_sym)); |
- |
- if (self->canonical) |
+ if (sym != NULL) |
{ |
- self->canonical->special_display = 1; |
- self->canonical->addr_string |
- = xstrprintf ("%s:%s", SYMBOL_NATURAL_NAME (fn_sym), |
- copy); |
+ VEC_safe_push (symbolp, result, sym); |
+ VEC_safe_push (symbolp, *label_funcs_ret, fn_sym); |
+ } |
+ } |
+ else |
+ { |
+ for (ix = 0; |
+ VEC_iterate (symbolp, function_symbols, ix, fn_sym); ++ix) |
+ { |
+ set_current_program_space (SYMTAB_PSPACE (SYMBOL_SYMTAB (fn_sym))); |
+ block = SYMBOL_BLOCK_VALUE (fn_sym); |
+ sym = lookup_symbol (name, block, LABEL_DOMAIN, 0); |
+ |
+ if (sym != NULL) |
+ { |
+ VEC_safe_push (symbolp, result, sym); |
+ VEC_safe_push (symbolp, *label_funcs_ret, fn_sym); |
+ } |
} |
+ } |
- *result = values; |
+ return result; |
+} |
- return 1; |
+ |
+ |
+/* A helper for create_sals_line_offset that handles the 'list_mode' case. */ |
+ |
+static void |
+decode_digits_list_mode (struct linespec_state *self, |
+ linespec_p ls, |
+ struct symtabs_and_lines *values, |
+ struct symtab_and_line val) |
+{ |
+ int ix; |
+ struct symtab *elt; |
+ |
+ gdb_assert (self->list_mode); |
+ |
+ for (ix = 0; VEC_iterate (symtab_p, ls->file_symtabs, ix, elt); |
+ ++ix) |
+ { |
+ /* The logic above should ensure this. */ |
+ gdb_assert (elt != NULL); |
+ |
+ set_current_program_space (SYMTAB_PSPACE (elt)); |
+ |
+ /* Simplistic search just for the list command. */ |
+ val.symtab = find_line_symtab (elt, val.line, NULL, NULL); |
+ if (val.symtab == NULL) |
+ val.symtab = elt; |
+ val.pspace = SYMTAB_PSPACE (elt); |
+ val.pc = 0; |
+ val.explicit_line = 1; |
+ |
+ add_sal_to_sals (self, values, &val, NULL); |
} |
+} |
+ |
+/* A helper for create_sals_line_offset that iterates over the symtabs, |
+ adding lines to the VEC. */ |
- result->sals = NULL; |
- result->nelts = 0; |
+static void |
+decode_digits_ordinary (struct linespec_state *self, |
+ linespec_p ls, |
+ int line, |
+ struct symtabs_and_lines *sals, |
+ struct linetable_entry **best_entry) |
+{ |
+ int ix; |
+ struct symtab *elt; |
- for (ix = 0; VEC_iterate (symbolp, function_symbols, ix, fn_sym); ++ix) |
+ for (ix = 0; VEC_iterate (symtab_p, ls->file_symtabs, ix, elt); ++ix) |
{ |
- struct block *block; |
- struct symbol *sym; |
+ int i; |
+ VEC (CORE_ADDR) *pcs; |
+ CORE_ADDR pc; |
+ |
+ /* The logic above should ensure this. */ |
+ gdb_assert (elt != NULL); |
- set_current_program_space (SYMTAB_PSPACE (SYMBOL_SYMTAB (fn_sym))); |
- block = SYMBOL_BLOCK_VALUE (fn_sym); |
- sym = lookup_symbol (copy, block, LABEL_DOMAIN, 0); |
+ set_current_program_space (SYMTAB_PSPACE (elt)); |
- if (sym != NULL) |
+ pcs = find_pcs_for_symtab_line (elt, line, best_entry); |
+ for (i = 0; VEC_iterate (CORE_ADDR, pcs, i, pc); ++i) |
{ |
struct symtab_and_line sal; |
- char *symname; |
- |
- symbol_to_sal (&sal, self->funfirstline, sym); |
- symname = xstrprintf ("%s:%s", |
- SYMBOL_NATURAL_NAME (fn_sym), |
- SYMBOL_NATURAL_NAME (sym)); |
- add_sal_to_sals (self, result, &sal, symname); |
- xfree (symname); |
+ |
+ init_sal (&sal); |
+ sal.pspace = SYMTAB_PSPACE (elt); |
+ sal.symtab = elt; |
+ sal.line = line; |
+ sal.pc = pc; |
+ add_sal_to_sals_basic (sals, &sal); |
} |
+ |
+ VEC_free (CORE_ADDR, pcs); |
} |
+} |
- if (self->canonical && result->nelts > 0) |
+ |
+ |
+/* Return the line offset represented by VARIABLE. */ |
+ |
+static struct line_offset |
+linespec_parse_variable (struct linespec_state *self, const char *variable) |
+{ |
+ int index = 0; |
+ const char *p; |
+ struct line_offset offset = {0, LINE_OFFSET_NONE}; |
+ |
+ p = (variable[1] == '$') ? variable + 2 : variable + 1; |
+ if (*p == '$') |
+ ++p; |
+ while (*p >= '0' && *p <= '9') |
+ ++p; |
+ if (!*p) /* Reached end of token without hitting non-digit. */ |
{ |
- self->canonical->pre_expanded = 1; |
- self->canonical->special_display = 1; |
+ /* We have a value history reference. */ |
+ struct value *val_history; |
- gdb_assert (self->user_function); |
- self->canonical->addr_string |
- = xstrprintf ("%s:%s", self->user_function, copy); |
+ sscanf ((variable[1] == '$') ? variable + 2 : variable + 1, "%d", &index); |
+ val_history |
+ = access_value_history ((variable[1] == '$') ? -index : index); |
+ if (TYPE_CODE (value_type (val_history)) != TYPE_CODE_INT) |
+ error (_("History values used in line " |
+ "specs must have integer values.")); |
+ offset.offset = value_as_long (val_history); |
+ } |
+ else |
+ { |
+ /* Not all digits -- may be user variable/function or a |
+ convenience variable. */ |
+ LONGEST valx; |
+ struct internalvar *ivar; |
+ |
+ /* Try it as a convenience variable. If it is not a convenience |
+ variable, return and allow normal symbol lookup to occur. */ |
+ ivar = lookup_only_internalvar (variable + 1); |
+ if (ivar == NULL) |
+ /* No internal variable with that name. Mark the offset |
+ as unknown to allow the name to be looked up as a symbol. */ |
+ offset.sign = LINE_OFFSET_UNKNOWN; |
+ else |
+ { |
+ /* We found a valid variable name. If it is not an integer, |
+ throw an error. */ |
+ if (!get_internalvar_integer (ivar, &valx)) |
+ error (_("Convenience variables used in line " |
+ "specs must have integer values.")); |
+ else |
+ offset.offset = valx; |
+ } |
} |
- return result->nelts > 0; |
+ return offset; |
} |
+ |
/* A callback used to possibly add a symbol to the results. */ |
@@ -2655,20 +3250,16 @@ static int |
collect_symbols (struct symbol *sym, void *data) |
{ |
struct collect_info *info = data; |
- struct symtab_and_line sal; |
- if (symbol_to_sal (&sal, info->state->funfirstline, sym) |
- && maybe_add_address (info->state->addr_set, |
- SYMTAB_PSPACE (SYMBOL_SYMTAB (sym)), |
- sal.pc)) |
- add_sal_to_sals (info->state, &info->result, &sal, |
- SYMBOL_NATURAL_NAME (sym)); |
- |
- return 1; |
+ /* In list mode, add all matching symbols, regardless of class. |
+ This allows the user to type "list a_global_variable". */ |
+ if (SYMBOL_CLASS (sym) == LOC_BLOCK || info->state->list_mode) |
+ VEC_safe_push (symbolp, info->result.symbols, sym); |
+ return 1; /* Continue iterating. */ |
} |
-/* We've found a minimal symbol MSYMBOL to associate with our |
- linespec; add it to the result symtabs_and_lines. */ |
+/* We've found a minimal symbol MSYMBOL in OBJFILE to associate with our |
+ linespec; return the SAL in RESULT. */ |
static void |
minsym_found (struct linespec_state *self, struct objfile *objfile, |
@@ -2696,17 +3287,6 @@ minsym_found (struct linespec_state *self, struct objfile *objfile, |
add_sal_to_sals (self, result, &sal, SYMBOL_NATURAL_NAME (msymbol)); |
} |
-/* A helper struct which just holds a minimal symbol and the object |
- file from which it came. */ |
- |
-typedef struct minsym_and_objfile |
-{ |
- struct minimal_symbol *minsym; |
- struct objfile *objfile; |
-} minsym_and_objfile_d; |
- |
-DEF_VEC_O (minsym_and_objfile_d); |
- |
/* A helper struct to pass some data through |
iterate_over_minimal_symbols. */ |
@@ -2859,8 +3439,8 @@ search_minsyms_for_name (struct collect_info *info, const char *name, |
if (classify_mtype (MSYMBOL_TYPE (item->minsym)) != classification) |
break; |
- minsym_found (info->state, item->objfile, item->minsym, |
- &info->result); |
+ VEC_safe_push (minsym_and_objfile_d, |
+ info->result.minimal_symbols, item); |
} |
} |
@@ -2880,15 +3460,13 @@ add_matching_symbols_to_info (const char *name, |
int ix; |
struct symtab *elt; |
- for (ix = 0; VEC_iterate (symtab_p, info->state->file_symtabs, ix, elt); ++ix) |
+ for (ix = 0; VEC_iterate (symtab_p, info->file_symtabs, ix, elt); ++ix) |
{ |
- struct symbol *sym; |
- |
if (elt == NULL) |
{ |
- iterate_over_all_matching_symtabs (name, VAR_DOMAIN, |
+ iterate_over_all_matching_symtabs (info->state, name, VAR_DOMAIN, |
collect_symbols, info, |
- pspace); |
+ pspace, 1); |
search_minsyms_for_name (info, name, pspace); |
} |
else if (pspace == NULL || pspace == SYMTAB_PSPACE (elt)) |
@@ -2904,67 +3482,6 @@ add_matching_symbols_to_info (const char *name, |
} |
} |
-/* Decode a linespec that's a variable. If FILE_SYMTAB is non-NULL, |
- look in that symtab's static variables first. */ |
- |
-static struct symtabs_and_lines |
-decode_variable (struct linespec_state *self, char *copy) |
-{ |
- struct collect_info info; |
- const char *lookup_name; |
- char *canon; |
- struct cleanup *cleanup; |
- |
- info.state = self; |
- info.result.sals = NULL; |
- info.result.nelts = 0; |
- |
- cleanup = demangle_for_lookup (copy, current_language->la_language, |
- &lookup_name); |
- if (current_language->la_language == language_ada) |
- { |
- /* In Ada, the symbol lookups are performed using the encoded |
- name rather than the demangled name. */ |
- lookup_name = ada_name_for_lookup (copy); |
- make_cleanup (xfree, (void *) lookup_name); |
- } |
- |
- canon = cp_canonicalize_string_no_typedefs (lookup_name); |
- if (canon != NULL) |
- { |
- make_cleanup (xfree, canon); |
- lookup_name = canon; |
- } |
- |
- add_matching_symbols_to_info (lookup_name, &info, NULL); |
- |
- if (info.result.nelts > 0) |
- { |
- if (self->canonical) |
- { |
- self->canonical->pre_expanded = 1; |
- if (self->user_filename) |
- self->canonical->addr_string |
- = xstrprintf ("%s:%s", self->user_filename, copy); |
- else |
- self->canonical->addr_string = xstrdup (copy); |
- } |
- return info.result; |
- } |
- |
- if (!have_full_symbols () |
- && !have_partial_symbols () |
- && !have_minimal_symbols ()) |
- throw_error (NOT_FOUND_ERROR, |
- _("No symbol table is loaded. Use the \"file\" command.")); |
- if (self->user_filename) |
- throw_error (NOT_FOUND_ERROR, _("Function \"%s\" not defined in \"%s\"."), |
- copy, self->user_filename); |
- else |
- throw_error (NOT_FOUND_ERROR, _("Function \"%s\" not defined."), copy); |
-} |
- |
- |
/* Now come some functions that are called from multiple places within |