/* Parser for linespec for the GNU debugger, GDB.
- Copyright (C) 1986-2005, 2007-2012 Free Software Foundation, Inc.
+ Copyright (C) 1986-2019 Free Software Foundation, Inc.
This file is part of GDB.
#include "block.h"
#include "objc-lang.h"
#include "linespec.h"
-#include "exceptions.h"
#include "language.h"
#include "interps.h"
#include "mi/mi-cmds.h"
#include "cli/cli-utils.h"
#include "filenames.h"
#include "ada-lang.h"
+#include "stack.h"
+#include "location.h"
+#include "gdbsupport/function-view.h"
+#include "gdbsupport/def-vector.h"
+#include <algorithm>
-typedef struct symtab *symtab_p;
-DEF_VEC_P (symtab_p);
+/* An enumeration of the various things a user might attempt to
+ complete for a linespec location. */
-typedef struct symbol *symbolp;
-DEF_VEC_P (symbolp);
+enum class linespec_complete_what
+{
+ /* Nothing, no possible completion. */
+ NOTHING,
+
+ /* A function/method name. Due to ambiguity between
+
+ (gdb) b source[TAB]
+ source_file.c
+ source_function
+
+ this can also indicate a source filename, iff we haven't seen a
+ separate source filename component, as in "b source.c:function". */
+ FUNCTION,
-typedef struct type *typep;
-DEF_VEC_P (typep);
+ /* A label symbol. E.g., break file.c:function:LABEL. */
+ LABEL,
+
+ /* An expression. E.g., "break foo if EXPR", or "break *EXPR". */
+ EXPRESSION,
+
+ /* A linespec keyword ("if"/"thread"/"task").
+ E.g., "break func threa<tab>". */
+ KEYWORD,
+};
/* An address entry is used to ensure that any given location is only
added to the result a single time. It holds an address and the
CORE_ADDR addr;
};
+/* 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 explicit location describing the SaLs. */
+ struct explicit_location explicit_loc;
+
+ /* The list of symtabs to search to which to limit the search. May not
+ be NULL. If explicit.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. */
+ std::vector<symtab *> *file_symtabs;
+
+ /* A list of matching function symbols and minimal symbols. Both lists
+ may be NULL (or empty) if no matching symbols were found. */
+ std::vector<block_symbol> *function_symbols;
+ std::vector<bound_minimal_symbol> *minimal_symbols;
+
+ /* 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
+ {
+ std::vector<block_symbol> *label_symbols;
+ std::vector<block_symbol> *function_symbols;
+ } labels;
+};
+typedef struct linespec *linespec_p;
+
+/* A canonical linespec represented as a symtab-related string.
+
+ Each entry represents the "SYMTAB:SUFFIX" linespec string.
+ SYMTAB can be converted for example by symtab_to_fullname or
+ symtab_to_filename_for_display as needed. */
+
+struct linespec_canonical_name
+{
+ /* Remaining text part of the linespec string. */
+ char *suffix;
+
+ /* If NULL then SUFFIX is the whole linespec string. */
+ struct symtab *symtab;
+};
+
/* 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;
+ /* If not NULL, the search is restricted to just this program
+ space. */
+ struct program_space *search_pspace;
+
/* The default symtab to use, if no other symtab is specified. */
struct symtab *default_symtab;
/* 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;
/* The 'canonical' value passed to decode_line_full, or NULL. */
struct linespec_result *canonical;
- /* Canonical strings that mirror the symtabs_and_lines result. */
- char **canonical_names;
+ /* Canonical strings that mirror the std::vector<symtab_and_line> result. */
+ struct linespec_canonical_name *canonical_names;
/* This is a set of address_entry objects which is used to prevent
duplicate symbols from being entered into the result. */
htab_t addr_set;
+
+ /* Are we building a linespec? */
+ int is_linespec;
};
/* This is a helper object that is used when collecting symbols into a
/* The linespec object in use. */
struct linespec_state *state;
+ /* A list of symtabs to which to restrict matches. */
+ std::vector<symtab *> *file_symtabs;
+
/* The result being accumulated. */
- struct symtabs_and_lines result;
+ struct
+ {
+ std::vector<block_symbol> *symbols;
+ std::vector<bound_minimal_symbol> *minimal_symbols;
+ } result;
+
+ /* Possibly add a symbol to the results. */
+ virtual bool add_symbol (block_symbol *bsym);
};
-/* Prototypes for local functions. */
+bool
+collect_info::add_symbol (block_symbol *bsym)
+{
+ /* In list mode, add all matching symbols, regardless of class.
+ This allows the user to type "list a_global_variable". */
+ if (SYMBOL_CLASS (bsym->symbol) == LOC_BLOCK || this->state->list_mode)
+ this->result.symbols->push_back (*bsym);
-static void initialize_defaults (struct symtab **default_symtab,
- int *default_line);
+ /* Continue iterating. */
+ return true;
+}
-static struct symtabs_and_lines decode_indirect (struct linespec_state *self,
- char **argptr);
+/* Custom collect_info for symbol_searcher. */
-static char *locate_first_half (char **argptr, int *is_quote_enclosed);
+struct symbol_searcher_collect_info
+ : collect_info
+{
+ bool add_symbol (block_symbol *bsym) override
+ {
+ /* Add everything. */
+ this->result.symbols->push_back (*bsym);
+
+ /* Continue iterating. */
+ return true;
+ }
+};
+
+/* Token types */
+
+enum ls_token_type
+{
+ /* A keyword */
+ LSTOKEN_KEYWORD = 0,
+
+ /* A colon "separator" */
+ LSTOKEN_COLON,
+
+ /* A string */
+ LSTOKEN_STRING,
+
+ /* 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. This is NULL-terminated so that it can be used
+ as enum completer. */
+const char * const linespec_keywords[] = { "if", "thread", "task", NULL };
+#define IF_KEYWORD_INDEX 0
-static struct symtabs_and_lines decode_objc (struct linespec_state *self,
- char **argptr);
+/* A token of the linespec lexer */
-static struct symtabs_and_lines decode_compound (struct linespec_state *self,
- char **argptr,
- char *saved_arg,
- char *p);
+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 linespec_parser
+{
+ linespec_parser (int flags, const struct language_defn *language,
+ struct program_space *search_pspace,
+ struct symtab *default_symtab,
+ int default_line,
+ struct linespec_result *canonical);
+
+ ~linespec_parser ();
-static VEC (symbolp) *lookup_prefix_sym (char **argptr, char *p,
- VEC (symtab_p) *,
- char **);
+ DISABLE_COPY_AND_ASSIGN (linespec_parser);
+
+ /* Lexer internal data */
+ struct
+ {
+ /* Save head of input stream. */
+ const char *saved_arg;
+
+ /* Head of the input stream. */
+ const char *stream;
+#define PARSER_STREAM(P) ((P)->lexer.stream)
+
+ /* The current token. */
+ linespec_token current;
+ } lexer {};
+
+ /* Is the entire linespec quote-enclosed? */
+ int is_quote_enclosed = 0;
+
+ /* The state of the parse. */
+ struct linespec_state state {};
+#define PARSER_STATE(PPTR) (&(PPTR)->state)
+
+ /* The result of the parse. */
+ struct linespec result {};
+#define PARSER_RESULT(PPTR) (&(PPTR)->result)
+
+ /* What the parser believes the current word point should complete
+ to. */
+ linespec_complete_what complete_what = linespec_complete_what::NOTHING;
+
+ /* The completion word point. The parser advances this as it skips
+ tokens. At some point the input string will end or parsing will
+ fail, and then we attempt completion at the captured completion
+ word point, interpreting the string at completion_word as
+ COMPLETE_WHAT. */
+ const char *completion_word = nullptr;
+
+ /* If the current token was a quoted string, then this is the
+ quoting character (either " or '). */
+ int completion_quote_char = 0;
+
+ /* If the current token was a quoted string, then this points at the
+ end of the quoted string. */
+ const char *completion_quote_end = nullptr;
+
+ /* If parsing for completion, then this points at the completion
+ tracker. Otherwise, this is NULL. */
+ struct completion_tracker *completion_tracker = nullptr;
+};
-static struct symtabs_and_lines find_method (struct linespec_state *self,
- char *saved_arg,
- char *copy,
- const char *class_name,
- VEC (symbolp) *sym_classes);
+/* A convenience macro for accessing the explicit location result of
+ the parser. */
+#define PARSER_EXPLICIT(PPTR) (&PARSER_RESULT ((PPTR))->explicit_loc)
-static void cplusplus_error (const char *name, const char *fmt, ...)
- ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (2, 3);
+/* Prototypes for local functions. */
-static char *find_toplevel_char (char *s, char c);
+static void iterate_over_file_blocks
+ (struct symtab *symtab, const lookup_name_info &name,
+ domain_enum domain,
+ gdb::function_view<symbol_found_callback_ftype> callback);
-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);
+CORE_ADDR linespec_expression_to_pc (const char **exp_ptr);
-static VEC (symbolp) *find_function_symbols (char **argptr, char *p,
- int is_quote_enclosed,
- char **user_function);
+static std::vector<symtab_and_line> decode_objc (struct linespec_state *self,
+ linespec_p ls,
+ const char *arg);
-static struct symtabs_and_lines decode_all_digits (struct linespec_state *self,
- char **argptr,
- char *q);
+static std::vector<symtab *> symtabs_from_filename
+ (const char *, struct program_space *pspace);
-static struct symtabs_and_lines decode_dollar (struct linespec_state *self,
- char *copy);
+static std::vector<block_symbol> *find_label_symbols
+ (struct linespec_state *self, std::vector<block_symbol> *function_symbols,
+ std::vector<block_symbol> *label_funcs_ret, const char *name,
+ bool completion_mode = false);
-static int decode_label (struct linespec_state *self,
- VEC (symbolp) *function_symbols,
- char *copy,
- struct symtabs_and_lines *result);
+static void find_linespec_symbols (struct linespec_state *self,
+ std::vector<symtab *> *file_symtabs,
+ const char *name,
+ symbol_name_match_type name_match_type,
+ std::vector<block_symbol> *symbols,
+ std::vector<bound_minimal_symbol> *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);
static void add_matching_symbols_to_info (const char *name,
+ symbol_name_match_type name_match_type,
+ enum search_domain search_domain,
struct collect_info *info,
struct program_space *pspace);
-static void add_all_symbol_names_from_pspace (struct collect_info *info,
- struct program_space *pspace,
- VEC (const_char_ptr) *names);
+static void add_all_symbol_names_from_pspace
+ (struct collect_info *info, struct program_space *pspace,
+ const std::vector<const char *> &names, enum search_domain search_domain);
-/* Helper functions. */
+static std::vector<symtab *>
+ collect_symtabs_from_filename (const char *file,
+ struct program_space *pspace);
-/* Add SAL to SALS. */
+static std::vector<symtab_and_line> decode_digits_ordinary
+ (struct linespec_state *self,
+ linespec_p ls,
+ int line,
+ 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 std::vector<symtab_and_line> decode_digits_list_mode
+ (struct linespec_state *self,
+ linespec_p ls,
+ 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,
+ std::vector<symtab_and_line> *result);
-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);
+static bool compare_symbols (const block_symbol &a, const block_symbol &b);
- if (self->canonical)
- {
- char *canonical_name = NULL;
+static bool compare_msymbols (const bound_minimal_symbol &a,
+ const bound_minimal_symbol &b);
- self->canonical_names = xrealloc (self->canonical_names,
- sals->nelts * sizeof (char *));
- if (sal->symtab && sal->symtab->filename)
- {
- char *filename = sal->symtab->filename;
+/* 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 = "\"\'";
- /* 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);
- }
+/* Lexer functions. */
- self->canonical_names[sals->nelts - 1] = canonical_name;
- }
-}
+/* Lex a number from the input in PARSER. This only supports
+ decimal numbers.
-/* A hash function for address_entry. */
+ Return true if input is decimal numbers. Return false if not. */
-static hashval_t
-hash_address_entry (const void *p)
+static int
+linespec_lexer_lex_number (linespec_parser *parser, linespec_token *tokenp)
{
- const struct address_entry *aep = p;
- hashval_t hash;
+ tokenp->type = LSTOKEN_NUMBER;
+ LS_TOKEN_STOKEN (*tokenp).length = 0;
+ LS_TOKEN_STOKEN (*tokenp).ptr = PARSER_STREAM (parser);
- hash = iterative_hash_object (aep->pspace, 0);
- return iterative_hash_object (aep->addr, hash);
-}
+ /* Keep any sign at the start of the stream. */
+ if (*PARSER_STREAM (parser) == '+' || *PARSER_STREAM (parser) == '-')
+ {
+ ++LS_TOKEN_STOKEN (*tokenp).length;
+ ++(PARSER_STREAM (parser));
+ }
-/* An equality function for address_entry. */
+ while (isdigit (*PARSER_STREAM (parser)))
+ {
+ ++LS_TOKEN_STOKEN (*tokenp).length;
+ ++(PARSER_STREAM (parser));
+ }
-static int
-eq_address_entry (const void *a, const void *b)
-{
- const struct address_entry *aea = a;
- const struct address_entry *aeb = b;
+ /* If the next character in the input buffer is not a space, comma,
+ quote, or colon, this 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 aea->pspace == aeb->pspace && aea->addr == aeb->addr;
+ return 1;
}
-/* Check whether the address, represented by PSPACE and ADDR, is
- already in the set. If so, return 0. Otherwise, add it and return
- 1. */
+/* See linespec.h. */
-static int
-maybe_add_address (htab_t set, struct program_space *pspace, CORE_ADDR addr)
+const char *
+linespec_lexer_lex_keyword (const char *p)
{
- struct address_entry e, *p;
- void **slot;
+ int i;
- e.pspace = pspace;
- e.addr = addr;
- slot = htab_find_slot (set, &e, INSERT);
- if (*slot)
- return 0;
+ if (p != NULL)
+ {
+ for (i = 0; linespec_keywords[i] != NULL; ++i)
+ {
+ int len = strlen (linespec_keywords[i]);
+
+ /* If P begins with one of the keywords and the next
+ character is whitespace, we may have found a keyword.
+ It is only a keyword if it is not followed by another
+ keyword. */
+ if (strncmp (p, linespec_keywords[i], len) == 0
+ && isspace (p[len]))
+ {
+ int j;
- p = XNEW (struct address_entry);
- memcpy (p, &e, sizeof (struct address_entry));
- *slot = p;
+ /* Special case: "if" ALWAYS stops the lexer, since it
+ is not possible to predict what is going to appear in
+ the condition, which can only be parsed after SaLs have
+ been found. */
+ if (i != IF_KEYWORD_INDEX)
+ {
+ p += len;
+ p = skip_spaces (p);
+ for (j = 0; linespec_keywords[j] != NULL; ++j)
+ {
+ int nextlen = strlen (linespec_keywords[j]);
+
+ if (strncmp (p, linespec_keywords[j], nextlen) == 0
+ && isspace (p[nextlen]))
+ return NULL;
+ }
+ }
- return 1;
+ return linespec_keywords[i];
+ }
+ }
+ }
+
+ return NULL;
}
-/* Issue a helpful hint on using the command completion feature on
- single quoted demangled C++ symbols as part of the completion
- error. */
+/* See description in linespec.h. */
-static void
-cplusplus_error (const char *name, const char *fmt, ...)
+int
+is_ada_operator (const char *string)
{
- struct ui_file *tmp_stream;
- char *message;
+ const struct ada_opname_map *mapping;
- tmp_stream = mem_fileopen ();
- make_cleanup_ui_file_delete (tmp_stream);
+ for (mapping = ada_opname_table;
+ mapping->encoded != NULL
+ && !startswith (string, mapping->decoded); ++mapping)
+ ;
- {
- va_list args;
+ return mapping->decoded == NULL ? 0 : strlen (mapping->decoded);
+}
- va_start (args, fmt);
- vfprintf_unfiltered (tmp_stream, fmt, args);
- va_end (args);
- }
+/* Find QUOTE_CHAR in STRING, accounting for the ':' terminal. Return
+ the location of QUOTE_CHAR, or NULL if not found. */
+
+static const char *
+skip_quote_char (const char *string, char quote_char)
+{
+ const char *p, *last;
- while (*name == '\'')
- name++;
- fprintf_unfiltered (tmp_stream,
- ("Hint: try '%s<TAB> or '%s<ESC-?>\n"
- "(Note leading single quote.)"),
- name, name);
+ 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++;
+ }
- message = ui_file_xstrdup (tmp_stream, NULL);
- make_cleanup (xfree, message);
- throw_error (NOT_FOUND_ERROR, "%s", message);
+ return last;
}
-/* Some data for the expand_symtabs_matching callback. */
+/* Make a writable copy of the string given in TOKEN, trimming
+ any trailing whitespace. */
-struct symbol_matcher_data
+static gdb::unique_xmalloc_ptr<char>
+copy_token_string (linespec_token token)
{
- /* 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;
-};
+ const char *str, *s;
-/* A helper for iterate_over_all_matching_symtabs that is passed as a
- callback to the expand_symtabs_matching method. */
+ if (token.type == LSTOKEN_KEYWORD)
+ return make_unique_xstrdup (LS_TOKEN_KEYWORD (token));
-static int
-iterate_name_matcher (const char *name, void *d)
-{
- const struct symbol_matcher_data *data = d;
+ str = LS_TOKEN_STOKEN (token).ptr;
+ s = remove_trailing_whitespace (str, str + LS_TOKEN_STOKEN (token).length);
- if (data->symbol_name_cmp (name, data->lookup_name) == 0)
- return 1; /* Expand this symbol's symbol table. */
- return 0; /* Skip this symbol. */
+ return gdb::unique_xmalloc_ptr<char> (savestring (str, s - str));
}
-/* 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. */
+/* Does P represent the end of a quote-enclosed linespec? */
-static void
-iterate_over_all_matching_symtabs (const char *name,
- const domain_enum domain,
- symbol_found_callback_ftype *callback,
- void *data,
- struct program_space *search_pspace)
+static int
+is_closing_quote_enclosed (const char *p)
{
- struct objfile *objfile;
- struct program_space *pspace;
- struct symbol_matcher_data matcher_data;
-
- matcher_data.lookup_name = name;
- matcher_data.symbol_name_cmp =
- current_language->la_get_symbol_name_cmp != NULL
- ? current_language->la_get_symbol_name_cmp (name)
- : strcmp_iw;
+ if (strchr (linespec_quote_characters, *p))
+ ++p;
+ p = skip_spaces ((char *) p);
+ return (*p == '\0' || linespec_lexer_lex_keyword (p));
+}
- ALL_PSPACES (pspace)
- {
- if (search_pspace != NULL && search_pspace != pspace)
- continue;
- if (pspace->executing_startup)
- continue;
+/* 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. */
- set_current_program_space (pspace);
+static const char *
+find_parameter_list_end (const char *input)
+{
+ char end_char, start_char;
+ int depth;
+ const char *p;
+
+ 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,
- &matcher_data);
-
- 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. */
+/* If the [STRING, STRING_LEN) string ends with what looks like a
+ keyword, return the keyword start offset in STRING. Return -1
+ otherwise. */
-static struct block *
-get_search_block (struct symtab *symtab)
+static size_t
+string_find_incomplete_keyword_at_end (const char * const *keywords,
+ const char *string, size_t string_len)
{
- struct block *block;
+ const char *end = string + string_len;
+ const char *p = end;
- if (symtab != NULL)
- block = BLOCKVECTOR_BLOCK (BLOCKVECTOR (symtab), STATIC_BLOCK);
- else
+ while (p > string && *p != ' ')
+ --p;
+ if (p > string)
{
- 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);
+ p++;
+ size_t len = end - p;
+ for (size_t i = 0; keywords[i] != NULL; ++i)
+ if (strncmp (keywords[i], p, len) == 0)
+ return p - string;
}
- return block;
+ return -1;
}
-/* 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. */
+/* Lex a string from the input in PARSER. */
-static void
-find_methods (struct type *t, const char *name,
- VEC (const_char_ptr) **result_names,
- VEC (typep) **superclasses)
+static linespec_token
+linespec_lexer_lex_string (linespec_parser *parser)
{
- int i1 = 0;
- int ibase;
- const char *class_name = type_name_no_tag (t);
+ linespec_token token;
+ const char *start = PARSER_STREAM (parser);
- /* 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)
+ 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)))
{
- int method_counter;
- int name_len = strlen (name);
+ const char *end;
+ char quote_char = *PARSER_STREAM (parser);
- CHECK_TYPEDEF (t);
+ /* Special case: Ada operators. */
+ if (PARSER_STATE (parser)->language->la_language == language_ada
+ && quote_char == '\"')
+ {
+ int len = is_ada_operator (PARSER_STREAM (parser));
- /* Loop over each method name. At this level, all overloads of a name
- are counted as a single name. There is an inner loop which loops over
- each overload. */
+ 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;
+ }
- for (method_counter = TYPE_NFN_FIELDS (t) - 1;
- method_counter >= 0;
- --method_counter)
+ /* The input does not represent an Ada operator -- fall through
+ to normal quoted string handling. */
+ }
+
+ /* Skip past the beginning quote. */
+ ++(PARSER_STREAM (parser));
+
+ /* 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);
+
+ /* This helps the completer mode decide whether we have a
+ complete string. */
+ parser->completion_quote_char = quote_char;
+ parser->completion_quote_end = end;
+
+ /* Error if the input did not terminate properly, unless in
+ completion mode. */
+ if (end == NULL)
{
- const char *method_name = TYPE_FN_FIELDLIST_NAME (t, method_counter);
- char dem_opname[64];
+ if (parser->completion_tracker == NULL)
+ error (_("unmatched quote"));
+
+ /* In completion mode, we'll try to complete the incomplete
+ token. */
+ token.type = LSTOKEN_STRING;
+ while (*PARSER_STREAM (parser) != '\0')
+ PARSER_STREAM (parser)++;
+ LS_TOKEN_STOKEN (token).length = PARSER_STREAM (parser) - 1 - start;
+ }
+ else
+ {
+ /* 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
+ {
+ const 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.
- if (strncmp (method_name, "__", 2) == 0 ||
- strncmp (method_name, "op", 2) == 0 ||
- strncmp (method_name, "type", 4) == 0)
+ This allows users to forgo quoting CV-qualifiers, template arguments,
+ and similar common language constructs. */
+
+ while (1)
+ {
+ if (isspace (*PARSER_STREAM (parser)))
{
- if (cplus_demangle_opname (method_name, dem_opname, DMGL_ANSI))
- method_name = dem_opname;
- else if (cplus_demangle_opname (method_name, dem_opname, 0))
- method_name = dem_opname;
+ 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 (strcmp_iw (method_name, name) == 0)
+ /* If the next character is EOI or (single) ':', the
+ string is complete; return the token. */
+ if (*PARSER_STREAM (parser) == 0)
{
- int field_counter;
+ 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));
- for (field_counter = (TYPE_FN_FIELDLIST_LENGTH (t, method_counter)
- - 1);
- field_counter >= 0;
- --field_counter)
+ /* Do not tokenize ABI tags such as "[abi:cxx11]". */
+ else if (PARSER_STREAM (parser) - start > 4
+ && startswith (PARSER_STREAM (parser) - 4, "[abi"))
{
- struct fn_field *f;
- const char *phys_name;
+ /* Nothing. */
+ }
- f = TYPE_FN_FIELDLIST1 (t, method_counter);
- if (TYPE_FN_FIELD_STUB (f, field_counter))
- continue;
- phys_name = TYPE_FN_FIELD_PHYSNAME (f, field_counter);
- VEC_safe_push (const_char_ptr, *result_names, phys_name);
+ /* 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) == '(')
+ {
+ /* Don't interpret 'operator<' / 'operator<<' as a
+ template parameter list though. */
+ if (*PARSER_STREAM (parser) == '<'
+ && (PARSER_STATE (parser)->language->la_language
+ == language_cplus)
+ && (PARSER_STREAM (parser) - start) >= CP_OPERATOR_LEN)
+ {
+ const char *op = PARSER_STREAM (parser);
+
+ while (op > start && isspace (op[-1]))
+ op--;
+ if (op - start >= CP_OPERATOR_LEN)
+ {
+ op -= CP_OPERATOR_LEN;
+ if (strncmp (op, CP_OPERATOR_STR, CP_OPERATOR_LEN) == 0
+ && (op == start
+ || !(isalnum (op[-1]) || op[-1] == '_')))
+ {
+ /* This is an operator name. Keep going. */
+ ++(PARSER_STREAM (parser));
+ if (*PARSER_STREAM (parser) == '<')
+ ++(PARSER_STREAM (parser));
+ continue;
+ }
+ }
}
+
+ const char *end = find_parameter_list_end (PARSER_STREAM (parser));
+ PARSER_STREAM (parser) = end;
+
+ /* Don't loop around to the normal \0 case above because
+ we don't want to misinterpret a potential keyword at
+ the end of the token when the string isn't
+ "()<>"-balanced. This handles "b
+ function(thread<tab>" in completion mode. */
+ if (*end == '\0')
+ {
+ LS_TOKEN_STOKEN (token).ptr = start;
+ LS_TOKEN_STOKEN (token).length
+ = PARSER_STREAM (parser) - start;
+ return token;
+ }
+ else
+ 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) > CP_OPERATOR_LEN)
+ {
+ const char *op = strstr (start, CP_OPERATOR_STR);
+
+ if (op != NULL && is_operator_name (op))
+ {
+ /* 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. */
+ gdb_assert (*(PARSER_STREAM (parser)) != '\0');
+ ++(PARSER_STREAM (parser));
}
}
- for (ibase = 0; ibase < TYPE_N_BASECLASSES (t); ibase++)
- VEC_safe_push (typep, *superclasses, TYPE_BASECLASS (t, ibase));
+ return token;
}
-/* Find an instance of the character C in the string S that is outside
- of all parenthesis pairs, single-quoted strings, and double-quoted
- strings. Also, ignore the char within a template name, like a ','
- within foo<int, int>. */
+/* Lex a single linespec token from PARSER. */
-static char *
-find_toplevel_char (char *s, char c)
+static linespec_token
+linespec_lexer_lex_one (linespec_parser *parser)
{
- int quoted = 0; /* zero if we're not in quotes;
- '"' if we're in a double-quoted string;
+ 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 = linespec_lexer_lex_keyword (PARSER_STREAM (parser));
+ if (keyword != NULL)
+ {
+ parser->lexer.current.type = LSTOKEN_KEYWORD;
+ LS_TOKEN_KEYWORD (parser->lexer.current) = keyword;
+ /* We do not advance the stream here intentionally:
+ we would like lexing to stop when a keyword is seen.
+
+ PARSER_STREAM (parser) += strlen (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. Also advance the completion word for completion
+ mode. */
+
+static linespec_token
+linespec_lexer_consume_token (linespec_parser *parser)
+{
+ gdb_assert (parser->lexer.current.type != LSTOKEN_EOI);
+
+ bool advance_word = (parser->lexer.current.type != LSTOKEN_STRING
+ || *PARSER_STREAM (parser) != '\0');
+
+ /* If we're moving past a string to some other token, it must be the
+ quote was terminated. */
+ if (parser->completion_quote_char)
+ {
+ gdb_assert (parser->lexer.current.type == LSTOKEN_STRING);
+
+ /* If the string was the last (non-EOI) token, we're past the
+ quote, but remember that for later. */
+ if (*PARSER_STREAM (parser) != '\0')
+ {
+ parser->completion_quote_char = '\0';
+ parser->completion_quote_end = NULL;;
+ }
+ }
+
+ parser->lexer.current.type = LSTOKEN_CONSUMED;
+ linespec_lexer_lex_one (parser);
+
+ if (parser->lexer.current.type == LSTOKEN_STRING)
+ {
+ /* Advance the completion word past a potential initial
+ quote-char. */
+ parser->completion_word = LS_TOKEN_STOKEN (parser->lexer.current).ptr;
+ }
+ else if (advance_word)
+ {
+ /* Advance the completion word past any whitespace. */
+ parser->completion_word = PARSER_STREAM (parser);
+ }
+
+ return parser->lexer.current;
+}
+
+/* Return the next token without consuming the current token. */
+
+static linespec_token
+linespec_lexer_peek_token (linespec_parser *parser)
+{
+ linespec_token next;
+ const char *saved_stream = PARSER_STREAM (parser);
+ linespec_token saved_token = parser->lexer.current;
+ int saved_completion_quote_char = parser->completion_quote_char;
+ const char *saved_completion_quote_end = parser->completion_quote_end;
+ const char *saved_completion_word = parser->completion_word;
+
+ next = linespec_lexer_consume_token (parser);
+ PARSER_STREAM (parser) = saved_stream;
+ parser->lexer.current = saved_token;
+ parser->completion_quote_char = saved_completion_quote_char;
+ parser->completion_quote_end = saved_completion_quote_end;
+ parser->completion_word = saved_completion_word;
+ return next;
+}
+
+/* Helper functions. */
+
+/* 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.
+
+ If LITERAL_CANONICAL is non-zero, SYMNAME will be used as the
+ canonical name for the SAL. */
+
+static void
+add_sal_to_sals (struct linespec_state *self,
+ std::vector<symtab_and_line> *sals,
+ struct symtab_and_line *sal,
+ const char *symname, int literal_canonical)
+{
+ sals->push_back (*sal);
+
+ if (self->canonical)
+ {
+ struct linespec_canonical_name *canonical;
+
+ self->canonical_names = XRESIZEVEC (struct linespec_canonical_name,
+ self->canonical_names,
+ sals->size ());
+ canonical = &self->canonical_names[sals->size () - 1];
+ if (!literal_canonical && sal->symtab)
+ {
+ symtab_to_fullname (sal->symtab);
+
+ /* 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->suffix = xstrprintf ("%s:%d", symname, sal->line);
+ else if (symname != NULL)
+ canonical->suffix = xstrdup (symname);
+ else
+ canonical->suffix = xstrprintf ("%d", sal->line);
+ canonical->symtab = sal->symtab;
+ }
+ else
+ {
+ if (symname != NULL)
+ canonical->suffix = xstrdup (symname);
+ else
+ canonical->suffix = xstrdup ("<unknown>");
+ canonical->symtab = NULL;
+ }
+ }
+}
+
+/* A hash function for address_entry. */
+
+static hashval_t
+hash_address_entry (const void *p)
+{
+ const struct address_entry *aep = (const struct address_entry *) 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 = (const struct address_entry *) a;
+ const struct address_entry *aeb = (const struct address_entry *) 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 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 true 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 lookup_name_info &lookup_name,
+ const domain_enum name_domain,
+ enum search_domain search_domain,
+ struct program_space *search_pspace, bool include_inline,
+ gdb::function_view<symbol_found_callback_ftype> callback)
+{
+ struct program_space *pspace;
+
+ ALL_PSPACES (pspace)
+ {
+ if (search_pspace != NULL && search_pspace != pspace)
+ continue;
+ if (pspace->executing_startup)
+ continue;
+
+ set_current_program_space (pspace);
+
+ for (objfile *objfile : current_program_space->objfiles ())
+ {
+ if (objfile->sf)
+ objfile->sf->qf->expand_symtabs_matching (objfile,
+ NULL,
+ lookup_name,
+ NULL, NULL,
+ search_domain);
+
+ for (compunit_symtab *cu : objfile->compunits ())
+ {
+ struct symtab *symtab = COMPUNIT_FILETABS (cu);
+
+ iterate_over_file_blocks (symtab, lookup_name, name_domain,
+ callback);
+
+ if (include_inline)
+ {
+ const struct block *block;
+ int i;
+
+ for (i = FIRST_LOCAL_BLOCK;
+ i < BLOCKVECTOR_NBLOCKS (SYMTAB_BLOCKVECTOR (symtab));
+ i++)
+ {
+ block = BLOCKVECTOR_BLOCK (SYMTAB_BLOCKVECTOR (symtab), i);
+ state->language->la_iterate_over_symbols
+ (block, lookup_name, name_domain,
+ [&] (block_symbol *bsym)
+ {
+ /* Restrict calls to CALLBACK to symbols
+ representing inline symbols only. */
+ if (SYMBOL_INLINED (bsym->symbol))
+ return callback (bsym);
+ return true;
+ });
+ }
+ }
+ }
+ }
+ }
+}
+
+/* Returns the block to be used for symbol searches from
+ the current location. */
+
+static const struct block *
+get_current_search_block (void)
+{
+ /* get_selected_block can change the current language when there is
+ no selected frame yet. */
+ scoped_restore_current_language save_language;
+ return get_selected_block (0);
+}
+
+/* Iterate over static and global blocks. */
+
+static void
+iterate_over_file_blocks
+ (struct symtab *symtab, const lookup_name_info &name,
+ domain_enum domain, gdb::function_view<symbol_found_callback_ftype> callback)
+{
+ const struct block *block;
+
+ for (block = BLOCKVECTOR_BLOCK (SYMTAB_BLOCKVECTOR (symtab), STATIC_BLOCK);
+ block != NULL;
+ block = BLOCK_SUPERBLOCK (block))
+ LA_ITERATE_OVER_SYMBOLS (block, name, domain, callback);
+}
+
+/* A helper for find_method. This finds all methods in type T of
+ language T_LANG 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, enum language t_lang, const char *name,
+ std::vector<const char *> *result_names,
+ std::vector<struct type *> *superclasses)
+{
+ int ibase;
+ const char *class_name = TYPE_NAME (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)
+ {
+ int method_counter;
+ lookup_name_info lookup_name (name, symbol_name_match_type::FULL);
+ symbol_name_matcher_ftype *symbol_name_compare
+ = get_symbol_name_matcher (language_def (t_lang), lookup_name);
+
+ t = check_typedef (t);
+
+ /* Loop over each method name. At this level, all overloads of a name
+ are counted as a single name. There is an inner loop which loops over
+ each overload. */
+
+ for (method_counter = TYPE_NFN_FIELDS (t) - 1;
+ method_counter >= 0;
+ --method_counter)
+ {
+ const char *method_name = TYPE_FN_FIELDLIST_NAME (t, method_counter);
+
+ if (symbol_name_compare (method_name, lookup_name, NULL))
+ {
+ int field_counter;
+
+ for (field_counter = (TYPE_FN_FIELDLIST_LENGTH (t, method_counter)
+ - 1);
+ field_counter >= 0;
+ --field_counter)
+ {
+ struct fn_field *f;
+ const char *phys_name;
+
+ f = TYPE_FN_FIELDLIST1 (t, method_counter);
+ if (TYPE_FN_FIELD_STUB (f, field_counter))
+ continue;
+ phys_name = TYPE_FN_FIELD_PHYSNAME (f, field_counter);
+ result_names->push_back (phys_name);
+ }
+ }
+ }
+ }
+
+ for (ibase = 0; ibase < TYPE_N_BASECLASSES (t); ibase++)
+ superclasses->push_back (TYPE_BASECLASS (t, ibase));
+}
+
+/* Find an instance of the character C in the string S that is outside
+ of all parenthesis pairs, single-quoted strings, and double-quoted
+ strings. Also, ignore the char within a template name, like a ','
+ within foo<int, int>, while considering C++ operator</operator<<. */
+
+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++)
{
depth++;
else if ((*scan == ')' || *scan == '>') && depth > 0)
depth--;
+ else if (*scan == 'o' && !quoted && depth == 0)
+ {
+ /* Handle C++ operator names. */
+ if (strncmp (scan, CP_OPERATOR_STR, CP_OPERATOR_LEN) == 0)
+ {
+ scan += CP_OPERATOR_LEN;
+ if (*scan == c)
+ return scan;
+ while (isspace (*scan))
+ {
+ ++scan;
+ if (*scan == c)
+ return scan;
+ }
+ if (*scan == '\0')
+ break;
+
+ switch (*scan)
+ {
+ /* Skip over one less than the appropriate number of
+ characters: the for loop will skip over the last
+ one. */
+ case '<':
+ if (scan[1] == '<')
+ {
+ scan++;
+ if (*scan == c)
+ return scan;
+ }
+ break;
+ case '>':
+ if (scan[1] == '>')
+ {
+ scan++;
+ if (*scan == c)
+ return scan;
+ }
+ break;
+ }
+ }
+ }
}
return 0;
}
-/* 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 (startswith (s, needle))
+ 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;
+}
+
+/* Convert CANONICAL to its string representation using
+ symtab_to_fullname for SYMTAB. */
+
+static std::string
+canonical_to_fullform (const struct linespec_canonical_name *canonical)
+{
+ if (canonical->symtab == NULL)
+ return canonical->suffix;
+ else
+ return string_printf ("%s:%s", symtab_to_fullname (canonical->symtab),
+ canonical->suffix);
}
/* Given FILTERS, a list of canonical names, filter the sals in RESULT
static void
filter_results (struct linespec_state *self,
- struct symtabs_and_lines *result,
- VEC (const_char_ptr) *filters)
+ std::vector<symtab_and_line> *result,
+ const std::vector<const char *> &filters)
{
- int i;
- const char *name;
-
- for (i = 0; VEC_iterate (const_char_ptr, filters, i, name); ++i)
+ for (const char *name : filters)
{
- struct linespec_sals lsal;
- int j;
+ linespec_sals lsal;
+
+ for (size_t j = 0; j < result->size (); ++j)
+ {
+ const struct linespec_canonical_name *canonical;
+
+ canonical = &self->canonical_names[j];
+ std::string fullform = canonical_to_fullform (canonical);
+
+ if (name == fullform)
+ lsal.sals.push_back ((*result)[j]);
+ }
+
+ if (!lsal.sals.empty ())
+ {
+ lsal.canonical = xstrdup (name);
+ self->canonical->lsals.push_back (std::move (lsal));
+ }
+ }
+
+ self->canonical->pre_expanded = 0;
+}
+
+/* Store RESULT into SELF->CANONICAL. */
+
+static void
+convert_results_to_lsals (struct linespec_state *self,
+ std::vector<symtab_and_line> *result)
+{
+ struct linespec_sals lsal;
+
+ lsal.canonical = NULL;
+ lsal.sals = std::move (*result);
+ self->canonical->lsals.push_back (std::move (lsal));
+}
+
+/* A structure that contains two string representations of a struct
+ linespec_canonical_name:
+ - one where the symtab's fullname is used;
+ - one where the filename followed the "set filename-display"
+ setting. */
+
+struct decode_line_2_item
+{
+ decode_line_2_item (std::string &&fullform_, std::string &&displayform_,
+ bool selected_)
+ : fullform (std::move (fullform_)),
+ displayform (std::move (displayform_)),
+ selected (selected_)
+ {
+ }
+
+ /* The form using symtab_to_fullname. */
+ std::string fullform;
+
+ /* The form using symtab_to_filename_for_display. */
+ std::string displayform;
+
+ /* Field is initialized to zero and it is set to one if the user
+ requested breakpoint for this entry. */
+ unsigned int selected : 1;
+};
+
+/* Helper for std::sort to sort decode_line_2_item entries by
+ DISPLAYFORM and secondarily by FULLFORM. */
+
+static bool
+decode_line_2_compare_items (const decode_line_2_item &a,
+ const decode_line_2_item &b)
+{
+ if (a.displayform != b.displayform)
+ return a.displayform < b.displayform;
+ return a.fullform < b.fullform;
+}
+
+/* Handle multiple results in RESULT depending on SELECT_MODE. This
+ will either return normally, throw an exception on multiple
+ results, or present a menu to the user. On return, the SALS vector
+ in SELF->CANONICAL is set up properly. */
+
+static void
+decode_line_2 (struct linespec_state *self,
+ std::vector<symtab_and_line> *result,
+ const char *select_mode)
+{
+ char *args;
+ const char *prompt;
+ int i;
+ std::vector<const char *> filters;
+ std::vector<struct decode_line_2_item> items;
+
+ gdb_assert (select_mode != multiple_symbols_all);
+ gdb_assert (self->canonical != NULL);
+ gdb_assert (!result->empty ());
+
+ /* Prepare ITEMS array. */
+ for (i = 0; i < result->size (); ++i)
+ {
+ const struct linespec_canonical_name *canonical;
+ std::string displayform;
+
+ canonical = &self->canonical_names[i];
+ gdb_assert (canonical->suffix != NULL);
+
+ std::string fullform = canonical_to_fullform (canonical);
+
+ if (canonical->symtab == NULL)
+ displayform = canonical->suffix;
+ else
+ {
+ const char *fn_for_display;
+
+ fn_for_display = symtab_to_filename_for_display (canonical->symtab);
+ displayform = string_printf ("%s:%s", fn_for_display,
+ canonical->suffix);
+ }
+
+ items.emplace_back (std::move (fullform), std::move (displayform),
+ false);
+ }
+
+ /* Sort the list of method names. */
+ std::sort (items.begin (), items.end (), decode_line_2_compare_items);
+
+ /* Remove entries with the same FULLFORM. */
+ items.erase (std::unique (items.begin (), items.end (),
+ [] (const struct decode_line_2_item &a,
+ const struct decode_line_2_item &b)
+ {
+ return a.fullform == b.fullform;
+ }),
+ items.end ());
+
+ if (select_mode == multiple_symbols_cancel && items.size () > 1)
+ error (_("canceled because the command is ambiguous\n"
+ "See set/show multiple-symbol."));
+
+ if (select_mode == multiple_symbols_all || items.size () == 1)
+ {
+ convert_results_to_lsals (self, result);
+ return;
+ }
+
+ printf_unfiltered (_("[0] cancel\n[1] all\n"));
+ for (i = 0; i < items.size (); i++)
+ printf_unfiltered ("[%d] %s\n", i + 2, items[i].displayform.c_str ());
+
+ prompt = getenv ("PS2");
+ if (prompt == NULL)
+ {
+ prompt = "> ";
+ }
+ args = command_line_input (prompt, "overload-choice");
+
+ if (args == 0 || *args == 0)
+ error_no_arg (_("one or more choice numbers"));
+
+ number_or_range_parser parser (args);
+ while (!parser.finished ())
+ {
+ int num = parser.get_number ();
+
+ 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. */
+ convert_results_to_lsals (self, result);
+ return;
+ }
+
+ num -= 2;
+ if (num >= items.size ())
+ printf_unfiltered (_("No choice number %d.\n"), num);
+ else
+ {
+ struct decode_line_2_item *item = &items[num];
+
+ if (!item->selected)
+ {
+ filters.push_back (item->fullform.c_str ());
+ item->selected = 1;
+ }
+ else
+ {
+ printf_unfiltered (_("duplicate request for %d ignored.\n"),
+ num + 2);
+ }
+ }
+ }
+
+ filter_results (self, result, filters);
+}
+
+\f
+
+/* 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 (const char *symbol, const 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)
+ {
+ gdb::unique_xmalloc_ptr<char> string = copy_token_string (token);
+ throw_error (GENERIC_ERROR,
+ _("malformed linespec error: unexpected %s, \"%s\""),
+ token_type_strings[token.type], string.get ());
+ }
+ else
+ throw_error (GENERIC_ERROR,
+ _("malformed linespec error: unexpected %s"),
+ token_type_strings[token.type]);
+}
+
+/* Throw an undefined label error. */
+
+static void ATTRIBUTE_NORETURN
+undefined_label_error (const char *function, const char *label)
+{
+ if (function != NULL)
+ throw_error (NOT_FOUND_ERROR,
+ _("No label \"%s\" defined in function \"%s\"."),
+ label, function);
+ else
+ throw_error (NOT_FOUND_ERROR,
+ _("No label \"%s\" defined in current function."),
+ label);
+}
+
+/* Throw a source file not found error. */
+
+static void ATTRIBUTE_NORETURN
+source_file_not_found_error (const char *name)
+{
+ throw_error (NOT_FOUND_ERROR, _("No source file named %s."), name);
+}
+
+/* Unless at EIO, save the current stream position as completion word
+ point, and consume the next token. */
+
+static linespec_token
+save_stream_and_consume_token (linespec_parser *parser)
+{
+ if (linespec_lexer_peek_token (parser).type != LSTOKEN_EOI)
+ parser->completion_word = PARSER_STREAM (parser);
+ return linespec_lexer_consume_token (parser);
+}
+
+/* See description in linespec.h. */
+
+struct line_offset
+linespec_parse_line_offset (const char *string)
+{
+ const char *start = 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;
+ }
+
+ if (*string != '\0' && !isdigit (*string))
+ error (_("malformed line offset: \"%s\""), start);
+
+ /* Right now, we only allow base 10 for offsets. */
+ line_offset.offset = atoi (string);
+ return line_offset;
+}
+
+/* In completion mode, if the user is still typing the number, there's
+ no possible completion to offer. But if there's already input past
+ the number, setup to expect NEXT. */
+
+static void
+set_completion_after_number (linespec_parser *parser,
+ linespec_complete_what next)
+{
+ if (*PARSER_STREAM (parser) == ' ')
+ {
+ parser->completion_word = skip_spaces (PARSER_STREAM (parser) + 1);
+ parser->complete_what = next;
+ }
+ else
+ {
+ parser->completion_word = PARSER_STREAM (parser);
+ parser->complete_what = linespec_complete_what::NOTHING;
+ }
+}
+
+/* Parse the basic_spec in PARSER's input. */
+
+static void
+linespec_parse_basic (linespec_parser *parser)
+{
+ gdb::unique_xmalloc_ptr<char> name;
+ linespec_token token;
+ std::vector<block_symbol> symbols;
+ std::vector<block_symbol> *labels;
+ std::vector<bound_minimal_symbol> minimal_symbols;
+
+ /* Get the next token. */
+ token = linespec_lexer_lex_one (parser);
+
+ /* If it is EOI or KEYWORD, issue an error. */
+ if (token.type == LSTOKEN_KEYWORD)
+ {
+ parser->complete_what = linespec_complete_what::NOTHING;
+ unexpected_linespec_error (parser);
+ }
+ else if (token.type == LSTOKEN_EOI)
+ {
+ unexpected_linespec_error (parser);
+ }
+ /* If it is a LSTOKEN_NUMBER, we have an offset. */
+ else if (token.type == LSTOKEN_NUMBER)
+ {
+ set_completion_after_number (parser, linespec_complete_what::KEYWORD);
+
+ /* Record the line offset and get the next token. */
+ name = copy_token_string (token);
+ PARSER_EXPLICIT (parser)->line_offset
+ = linespec_parse_line_offset (name.get ());
+
+ /* 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)
+ {
+ parser->complete_what = linespec_complete_what::NOTHING;
+ 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)
+ {
+ parser->complete_what = linespec_complete_what::NOTHING;
+ unexpected_linespec_error (parser);
+ }
+
+ /* The current token will contain the name of a function, method,
+ or label. */
+ name = copy_token_string (token);
+
+ if (parser->completion_tracker != NULL)
+ {
+ /* If the function name ends with a ":", then this may be an
+ incomplete "::" scope operator instead of a label separator.
+ E.g.,
+ "b klass:<tab>"
+ which should expand to:
+ "b klass::method()"
+
+ Do a tentative completion assuming the later. If we find
+ completions, advance the stream past the colon token and make
+ it part of the function name/token. */
+
+ if (!parser->completion_quote_char
+ && strcmp (PARSER_STREAM (parser), ":") == 0)
+ {
+ completion_tracker tmp_tracker;
+ const char *source_filename
+ = PARSER_EXPLICIT (parser)->source_filename;
+ symbol_name_match_type match_type
+ = PARSER_EXPLICIT (parser)->func_name_match_type;
+
+ linespec_complete_function (tmp_tracker,
+ parser->completion_word,
+ match_type,
+ source_filename);
+
+ if (tmp_tracker.have_completions ())
+ {
+ PARSER_STREAM (parser)++;
+ LS_TOKEN_STOKEN (token).length++;
+
+ name.reset (savestring (parser->completion_word,
+ (PARSER_STREAM (parser)
+ - parser->completion_word)));
+ }
+ }
+
+ PARSER_EXPLICIT (parser)->function_name = name.release ();
+ }
+ else
+ {
+ /* Try looking it up as a function/method. */
+ find_linespec_symbols (PARSER_STATE (parser),
+ PARSER_RESULT (parser)->file_symtabs, name.get (),
+ PARSER_EXPLICIT (parser)->func_name_match_type,
+ &symbols, &minimal_symbols);
+
+ if (!symbols.empty () || !minimal_symbols.empty ())
+ {
+ PARSER_RESULT (parser)->function_symbols
+ = new std::vector<block_symbol> (std::move (symbols));
+ PARSER_RESULT (parser)->minimal_symbols
+ = new std::vector<bound_minimal_symbol>
+ (std::move (minimal_symbols));
+ PARSER_EXPLICIT (parser)->function_name = name.release ();
+ }
+ else
+ {
+ /* NAME was not a function or a method. So it must be a label
+ name or user specified variable like "break foo.c:$zippo". */
+ labels = find_label_symbols (PARSER_STATE (parser), NULL,
+ &symbols, name.get ());
+ if (labels != NULL)
+ {
+ PARSER_RESULT (parser)->labels.label_symbols = labels;
+ PARSER_RESULT (parser)->labels.function_symbols
+ = new std::vector<block_symbol> (std::move (symbols));
+ PARSER_EXPLICIT (parser)->label_name = name.release ();
+ }
+ else if (token.type == LSTOKEN_STRING
+ && *LS_TOKEN_STOKEN (token).ptr == '$')
+ {
+ /* User specified a convenience variable or history value. */
+ PARSER_EXPLICIT (parser)->line_offset
+ = linespec_parse_variable (PARSER_STATE (parser), name.get ());
+
+ if (PARSER_EXPLICIT (parser)->line_offset.sign == LINE_OFFSET_UNKNOWN)
+ {
+ /* The user-specified variable was not valid. Do not
+ throw an error here. parse_linespec will do it for us. */
+ PARSER_EXPLICIT (parser)->function_name = name.release ();
+ return;
+ }
+ }
+ 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_EXPLICIT (parser)->function_name = name.release ();
+ return;
+ }
+ }
+ }
+
+ int previous_qc = parser->completion_quote_char;
+
+ /* Get the next token. */
+ token = linespec_lexer_consume_token (parser);
+
+ if (token.type == LSTOKEN_EOI)
+ {
+ if (previous_qc && !parser->completion_quote_char)
+ parser->complete_what = linespec_complete_what::KEYWORD;
+ }
+ else 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. */
+ set_completion_after_number (parser, linespec_complete_what::KEYWORD);
+
+ name = copy_token_string (token);
+ PARSER_EXPLICIT (parser)->line_offset
+ = linespec_parse_line_offset (name.get ());
+
+ /* Get the next token. */
+ token = linespec_lexer_consume_token (parser);
+ }
+ else if (token.type == LSTOKEN_EOI && parser->completion_tracker != NULL)
+ {
+ parser->complete_what = linespec_complete_what::LABEL;
+ }
+ else if (token.type == LSTOKEN_STRING)
+ {
+ parser->complete_what = linespec_complete_what::LABEL;
+
+ /* If we have text after the label separated by whitespace
+ (e.g., "b func():lab i<tab>"), don't consider it part of
+ the label. In completion mode that should complete to
+ "if", in normal mode, the 'i' should be treated as
+ garbage. */
+ if (parser->completion_quote_char == '\0')
+ {
+ const char *ptr = LS_TOKEN_STOKEN (token).ptr;
+ for (size_t i = 0; i < LS_TOKEN_STOKEN (token).length; i++)
+ {
+ if (ptr[i] == ' ')
+ {
+ LS_TOKEN_STOKEN (token).length = i;
+ PARSER_STREAM (parser) = skip_spaces (ptr + i + 1);
+ break;
+ }
+ }
+ }
+
+ if (parser->completion_tracker != NULL)
+ {
+ if (PARSER_STREAM (parser)[-1] == ' ')
+ {
+ parser->completion_word = PARSER_STREAM (parser);
+ parser->complete_what = linespec_complete_what::KEYWORD;
+ }
+ }
+ else
+ {
+ /* Grab a copy of the label's name and look it up. */
+ name = copy_token_string (token);
+ labels
+ = find_label_symbols (PARSER_STATE (parser),
+ PARSER_RESULT (parser)->function_symbols,
+ &symbols, name.get ());
+
+ if (labels != NULL)
+ {
+ PARSER_RESULT (parser)->labels.label_symbols = labels;
+ PARSER_RESULT (parser)->labels.function_symbols
+ = new std::vector<block_symbol> (std::move (symbols));
+ PARSER_EXPLICIT (parser)->label_name = name.release ();
+ }
+ else
+ {
+ /* We don't know what it was, but it isn't a label. */
+ undefined_label_error
+ (PARSER_EXPLICIT (parser)->function_name, name.get ());
+ }
+
+ }
+
+ /* Check for a line offset. */
+ token = save_stream_and_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);
+
+ /* Record the line offset and get the next token. */
+ name = copy_token_string (token);
+
+ PARSER_EXPLICIT (parser)->line_offset
+ = linespec_parse_line_offset (name.get ());
+
+ /* Get the next token. */
+ token = linespec_lexer_consume_token (parser);
+ }
+ }
+ else
+ {
+ /* Trailing ':' in the input. Issue an error. */
+ unexpected_linespec_error (parser);
+ }
+ }
+}
+
+/* Canonicalize the linespec contained in LS. The result is saved into
+ STATE->canonical. This function handles both linespec and explicit
+ locations. */
+
+static void
+canonicalize_linespec (struct linespec_state *state, const linespec_p ls)
+{
+ struct event_location *canon;
+ struct explicit_location *explicit_loc;
+
+ /* If canonicalization was not requested, no need to do anything. */
+ if (!state->canonical)
+ return;
+
+ /* Save everything as an explicit location. */
+ state->canonical->location
+ = new_explicit_location (&ls->explicit_loc);
+ canon = state->canonical->location.get ();
+ explicit_loc = get_explicit_location (canon);
+
+ if (explicit_loc->label_name != NULL)
+ {
+ state->canonical->special_display = 1;
+
+ if (explicit_loc->function_name == NULL)
+ {
+ /* No function was specified, so add the symbol name. */
+ gdb_assert (!ls->labels.function_symbols->empty ()
+ && (ls->labels.function_symbols->size () == 1));
+ block_symbol s = ls->labels.function_symbols->front ();
+ explicit_loc->function_name
+ = xstrdup (SYMBOL_NATURAL_NAME (s.symbol));
+ }
+ }
+
+ /* If this location originally came from a linespec, save a string
+ representation of it for display and saving to file. */
+ if (state->is_linespec)
+ {
+ char *linespec = explicit_location_to_linespec (explicit_loc);
+
+ set_event_location_string (canon, linespec);
+ xfree (linespec);
+ }
+}
+
+/* Given a line offset in LS, construct the relevant SALs. */
+
+static std::vector<symtab_and_line>
+create_sals_line_offset (struct linespec_state *self,
+ linespec_p ls)
+{
+ int use_default = 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 name, since
+ set_default_source_symtab_and_line uses
+ select_source_symtab that calls us with such an argument. */
+
+ if (ls->file_symtabs->size () == 1
+ && ls->file_symtabs->front () == nullptr)
+ {
+ set_current_program_space (self->program_space);
+
+ /* Make sure we have at least a default source line. */
+ set_default_source_symtab_and_line ();
+ initialize_defaults (&self->default_symtab, &self->default_line);
+ *ls->file_symtabs
+ = collect_symtabs_from_filename (self->default_symtab->filename,
+ self->search_pspace);
+ use_default = 1;
+ }
+
+ symtab_and_line val;
+ val.line = ls->explicit_loc.line_offset.offset;
+ switch (ls->explicit_loc.line_offset.sign)
+ {
+ case LINE_OFFSET_PLUS:
+ if (ls->explicit_loc.line_offset.offset == 0)
+ val.line = 5;
+ if (use_default)
+ val.line = self->default_line + val.line;
+ break;
+
+ case LINE_OFFSET_MINUS:
+ if (ls->explicit_loc.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. */
+ }
+
+ std::vector<symtab_and_line> values;
+ if (self->list_mode)
+ values = decode_digits_list_mode (self, ls, val);
+ else
+ {
+ struct linetable_entry *best_entry = NULL;
+ int i, j;
+
+ std::vector<symtab_and_line> intermediate_results
+ = decode_digits_ordinary (self, ls, val.line, &best_entry);
+ if (intermediate_results.empty () && best_entry != NULL)
+ intermediate_results = decode_digits_ordinary (self, ls,
+ best_entry->line,
+ &best_entry);
+
+ /* 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. */
+
+ gdb::def_vector<int> filter (intermediate_results.size ());
+ gdb::def_vector<const block *> blocks (intermediate_results.size ());
+
+ for (i = 0; i < intermediate_results.size (); ++i)
+ {
+ set_current_program_space (intermediate_results[i].pspace);
+
+ filter[i] = 1;
+ blocks[i] = block_for_pc_sect (intermediate_results[i].pc,
+ intermediate_results[i].section);
+ }
+
+ for (i = 0; i < intermediate_results.size (); ++i)
+ {
+ if (blocks[i] != NULL)
+ for (j = i + 1; j < intermediate_results.size (); ++j)
+ {
+ if (blocks[j] == blocks[i])
+ {
+ filter[j] = 0;
+ break;
+ }
+ }
+ }
- memset (&lsal, 0, sizeof (lsal));
+ for (i = 0; i < intermediate_results.size (); ++i)
+ if (filter[i])
+ {
+ struct symbol *sym = (blocks[i]
+ ? block_containing_function (blocks[i])
+ : NULL);
- for (j = 0; j < result->nelts; ++j)
- {
- if (strcmp (name, self->canonical_names[j]) == 0)
- add_sal_to_sals_basic (&lsal.sals, &result->sals[j]);
- }
+ if (self->funfirstline)
+ skip_prologue_sal (&intermediate_results[i]);
+ intermediate_results[i].symbol = sym;
+ add_sal_to_sals (self, &values, &intermediate_results[i],
+ sym ? SYMBOL_NATURAL_NAME (sym) : NULL, 0);
+ }
+ }
- if (lsal.sals.nelts > 0)
- {
- lsal.canonical = xstrdup (name);
- VEC_safe_push (linespec_sals, self->canonical->sals, &lsal);
- }
+ if (values.empty ())
+ {
+ if (ls->explicit_loc.source_filename)
+ throw_error (NOT_FOUND_ERROR, _("No line %d in file \"%s\"."),
+ val.line, ls->explicit_loc.source_filename);
+ else
+ throw_error (NOT_FOUND_ERROR, _("No line %d in the current file."),
+ val.line);
}
- self->canonical->pre_expanded = 0;
+ return values;
}
-/* Store RESULT into SELF->CANONICAL. */
+/* Convert the given ADDRESS into SaLs. */
-static void
-convert_results_to_lsals (struct linespec_state *self,
- struct symtabs_and_lines *result)
+static std::vector<symtab_and_line>
+convert_address_location_to_sals (struct linespec_state *self,
+ CORE_ADDR address)
{
- struct linespec_sals lsal;
+ symtab_and_line sal = find_pc_line (address, 0);
+ sal.pc = address;
+ sal.section = find_pc_overlay (address);
+ sal.explicit_pc = 1;
+ sal.symbol = find_pc_sect_containing_function (sal.pc, sal.section);
- lsal.canonical = NULL;
- lsal.sals = *result;
- VEC_safe_push (linespec_sals, self->canonical->sals, &lsal);
+ std::vector<symtab_and_line> sals;
+ add_sal_to_sals (self, &sals, &sal, core_addr_to_string (address), 1);
+
+ return sals;
}
-/* Handle multiple results in RESULT depending on SELECT_MODE. This
- will either return normally, throw an exception on multiple
- results, or present a menu to the user. On return, the SALS vector
- in SELF->CANONICAL is set up properly. */
+/* Create and return SALs from the linespec LS. */
-static void
-decode_line_2 (struct linespec_state *self,
- struct symtabs_and_lines *result,
- const char *select_mode)
+static std::vector<symtab_and_line>
+convert_linespec_to_sals (struct linespec_state *state, linespec_p ls)
{
- const char *iter;
- char *args, *prompt;
- int i;
- struct cleanup *old_chain;
- VEC (const_char_ptr) *item_names = NULL, *filters = NULL;
- struct get_number_or_range_state state;
-
- gdb_assert (select_mode != multiple_symbols_all);
- gdb_assert (self->canonical != NULL);
+ std::vector<symtab_and_line> sals;
- old_chain = make_cleanup (VEC_cleanup (const_char_ptr), &item_names);
- make_cleanup (VEC_cleanup (const_char_ptr), &filters);
- for (i = 0; i < result->nelts; ++i)
+ if (ls->labels.label_symbols != NULL)
{
- int j, found = 0;
- const char *iter;
+ /* We have just a bunch of functions/methods or labels. */
+ struct symtab_and_line sal;
- gdb_assert (self->canonical_names[i] != NULL);
- for (j = 0; VEC_iterate (const_char_ptr, item_names, j, iter); ++j)
+ for (const auto &sym : *ls->labels.label_symbols)
{
- if (strcmp (iter, self->canonical_names[i]) == 0)
- {
- found = 1;
- break;
- }
- }
-
- if (!found)
- VEC_safe_push (const_char_ptr, item_names, self->canonical_names[i]);
- }
-
- if (select_mode == multiple_symbols_cancel
- && VEC_length (const_char_ptr, item_names) > 1)
- error (_("canceled because the command is ambiguous\n"
- "See set/show multiple-symbol."));
-
- if (select_mode == multiple_symbols_all
- || VEC_length (const_char_ptr, item_names) == 1)
- {
- do_cleanups (old_chain);
- convert_results_to_lsals (self, result);
- 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);
+ struct program_space *pspace
+ = SYMTAB_PSPACE (symbol_symtab (sym.symbol));
- prompt = getenv ("PS2");
- if (prompt == NULL)
- {
- prompt = "> ";
+ if (symbol_to_sal (&sal, state->funfirstline, sym.symbol)
+ && maybe_add_address (state->addr_set, pspace, sal.pc))
+ add_sal_to_sals (state, &sals, &sal,
+ SYMBOL_NATURAL_NAME (sym.symbol), 0);
+ }
}
- 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)
+ else if (ls->function_symbols != NULL || ls->minimal_symbols != NULL)
{
- int num;
+ /* We have just a bunch of functions and/or methods. */
+ if (ls->function_symbols != NULL)
+ {
+ /* Sort symbols so that symbols with the same program space are next
+ to each other. */
+ std::sort (ls->function_symbols->begin (),
+ ls->function_symbols->end (),
+ compare_symbols);
- num = get_number_or_range (&state);
+ for (const auto &sym : *ls->function_symbols)
+ {
+ program_space *pspace
+ = SYMTAB_PSPACE (symbol_symtab (sym.symbol));
+ set_current_program_space (pspace);
+
+ /* Don't skip to the first line of the function if we
+ had found an ifunc minimal symbol for this function,
+ because that means that this function is an ifunc
+ resolver with the same name as the ifunc itself. */
+ bool found_ifunc = false;
+
+ if (state->funfirstline
+ && ls->minimal_symbols != NULL
+ && SYMBOL_CLASS (sym.symbol) == LOC_BLOCK)
+ {
+ const CORE_ADDR addr
+ = BLOCK_ENTRY_PC (SYMBOL_BLOCK_VALUE (sym.symbol));
+
+ for (const auto &elem : *ls->minimal_symbols)
+ {
+ if (MSYMBOL_TYPE (elem.minsym) == mst_text_gnu_ifunc
+ || MSYMBOL_TYPE (elem.minsym) == mst_data_gnu_ifunc)
+ {
+ CORE_ADDR msym_addr = BMSYMBOL_VALUE_ADDRESS (elem);
+ if (MSYMBOL_TYPE (elem.minsym) == mst_data_gnu_ifunc)
+ {
+ struct gdbarch *gdbarch
+ = get_objfile_arch (elem.objfile);
+ msym_addr
+ = (gdbarch_convert_from_func_ptr_addr
+ (gdbarch,
+ msym_addr,
+ current_top_target ()));
+ }
+
+ if (msym_addr == addr)
+ {
+ found_ifunc = true;
+ break;
+ }
+ }
+ }
+ }
- 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;
+ if (!found_ifunc)
+ {
+ symtab_and_line sal;
+ if (symbol_to_sal (&sal, state->funfirstline, sym.symbol)
+ && maybe_add_address (state->addr_set, pspace, sal.pc))
+ add_sal_to_sals (state, &sals, &sal,
+ SYMBOL_NATURAL_NAME (sym.symbol), 0);
+ }
+ }
}
- num -= 2;
- if (num >= VEC_length (const_char_ptr, item_names))
- printf_unfiltered (_("No choice number %d.\n"), num);
- else
+ if (ls->minimal_symbols != NULL)
{
- const char *elt = VEC_index (const_char_ptr, item_names, num);
+ /* Sort minimal symbols by program space, too */
+ std::sort (ls->minimal_symbols->begin (),
+ ls->minimal_symbols->end (),
+ compare_msymbols);
- if (elt != NULL)
+ for (const auto &elem : *ls->minimal_symbols)
{
- 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);
+ program_space *pspace = elem.objfile->pspace;
+ set_current_program_space (pspace);
+ minsym_found (state, elem.objfile, elem.minsym, &sals);
}
}
}
+ else if (ls->explicit_loc.line_offset.sign != LINE_OFFSET_UNKNOWN)
+ {
+ /* Only an offset was specified. */
+ sals = create_sals_line_offset (state, ls);
- filter_results (self, result, filters);
- do_cleanups (old_chain);
-}
+ /* Make sure we have a filename for canonicalization. */
+ if (ls->explicit_loc.source_filename == NULL)
+ {
+ const char *fullname = symtab_to_fullname (state->default_symtab);
-/* Valid delimiters for linespec keywords "if", "thread" or "task". */
+ /* It may be more appropriate to keep DEFAULT_SYMTAB in its symtab
+ form so that displaying SOURCE_FILENAME can follow the current
+ FILENAME_DISPLAY_STRING setting. But as it is used only rarely
+ it has been kept for code simplicity only in absolute form. */
+ ls->explicit_loc.source_filename = xstrdup (fullname);
+ }
+ }
+ else
+ {
+ /* We haven't found any results... */
+ return sals;
+ }
-static int
-is_linespec_boundary (char c)
-{
- return c == ' ' || c == '\t' || c == '\0' || c == ',';
+ canonicalize_linespec (state, ls);
+
+ if (!sals.empty () && state->canonical != NULL)
+ state->canonical->pre_expanded = 1;
+
+ return sals;
}
-/* 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 *)".
+/* Build RESULT from the explicit location components SOURCE_FILENAME,
+ FUNCTION_NAME, LABEL_NAME and LINE_OFFSET. */
- 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)
+static void
+convert_explicit_location_to_linespec (struct linespec_state *self,
+ linespec_p result,
+ const char *source_filename,
+ const char *function_name,
+ symbol_name_match_type fname_match_type,
+ const char *label_name,
+ struct line_offset line_offset)
{
- int depth = 0;
+ std::vector<block_symbol> symbols;
+ std::vector<block_symbol> *labels;
+ std::vector<bound_minimal_symbol> minimal_symbols;
- gdb_assert (*p == '(');
+ result->explicit_loc.func_name_match_type = fname_match_type;
- while (*p)
+ if (source_filename != NULL)
{
- if (*p == '(')
- ++depth;
- else if (*p == ')')
+ try
{
- if (--depth == 0)
- {
- ++p;
- break;
- }
+ *result->file_symtabs
+ = symtabs_from_filename (source_filename, self->search_pspace);
}
- ++p;
+ catch (const gdb_exception_error &except)
+ {
+ source_file_not_found_error (source_filename);
+ }
+ result->explicit_loc.source_filename = xstrdup (source_filename);
+ }
+ else
+ {
+ /* A NULL entry means to use the default symtab. */
+ result->file_symtabs->push_back (nullptr);
}
- return p;
-}
-
-/* Keep important information used when looking up a name. This includes
- template parameters, overload information, and important keywords, including
- the possible Java trailing type. */
-
-static char *
-keep_name_info (char *p, int on_boundary)
-{
- const char *quotes = get_gdb_completer_quote_characters ();
- char *saved_p = p;
- int nest = 0;
-
- while (*p)
+ if (function_name != NULL)
{
- if (strchr (quotes, *p))
- break;
+ find_linespec_symbols (self, result->file_symtabs,
+ function_name, fname_match_type,
+ &symbols, &minimal_symbols);
+
+ if (symbols.empty () && minimal_symbols.empty ())
+ symbol_not_found_error (function_name,
+ result->explicit_loc.source_filename);
+
+ result->explicit_loc.function_name = xstrdup (function_name);
+ result->function_symbols
+ = new std::vector<block_symbol> (std::move (symbols));
+ result->minimal_symbols
+ = new std::vector<bound_minimal_symbol> (std::move (minimal_symbols));
+ }
- if (*p == ',' && !nest)
- break;
+ if (label_name != NULL)
+ {
+ labels = find_label_symbols (self, result->function_symbols,
+ &symbols, label_name);
- if (on_boundary && !nest)
- {
- const char *const words[] = { "if", "thread", "task" };
- int wordi;
+ if (labels == NULL)
+ undefined_label_error (result->explicit_loc.function_name,
+ label_name);
- 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;
- }
+ result->explicit_loc.label_name = xstrdup (label_name);
+ result->labels.label_symbols = labels;
+ result->labels.function_symbols
+ = new std::vector<block_symbol> (std::move (symbols));
+ }
- if (*p == '(' || *p == '<' || *p == '[')
- nest++;
- else if ((*p == ')' || *p == '>' || *p == ']') && nest > 0)
- nest--;
+ if (line_offset.sign != LINE_OFFSET_UNKNOWN)
+ result->explicit_loc.line_offset = line_offset;
+}
- p++;
+/* Convert the explicit location EXPLICIT_LOC into SaLs. */
- /* The ',' check could fail on "operator ,". */
- p += cp_validate_operator (p);
+static std::vector<symtab_and_line>
+convert_explicit_location_to_sals (struct linespec_state *self,
+ linespec_p result,
+ const struct explicit_location *explicit_loc)
+{
+ convert_explicit_location_to_linespec (self, result,
+ explicit_loc->source_filename,
+ explicit_loc->function_name,
+ explicit_loc->func_name_match_type,
+ explicit_loc->label_name,
+ explicit_loc->line_offset);
+ return convert_linespec_to_sals (self, result);
+}
- on_boundary = is_linespec_boundary (p[-1]);
- }
+/* Parse a string that specifies a linespec.
- while (p > saved_p && is_linespec_boundary (p[-1]))
- p--;
+ The basic grammar of linespecs:
- return p;
-}
+ linespec -> var_spec | basic_spec
+ var_spec -> '$' (STRING | NUMBER)
-\f
-/* The parser of linespec itself. */
+ 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
-/* Parse a string that specifies a line number.
- Pass the address of a char * variable; that variable will be
- advanced over the characters actually parsed.
+ opt_file_spec -> "" | file_name_spec ':'
+ opt_label_spec -> "" | ':' label_name_spec
- The string can be:
+ file_name_spec -> STRING
+ function_name_spec -> STRING
+ label_name_spec -> STRING
+ function_name_spec -> STRING
+ offset_spec -> NUMBER
+ -> '+' NUMBER
+ -> '-' 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.
+ This may all be followed by several keywords such as "if EXPR",
+ which we ignore.
- This may all be followed by an "if EXPR", which we ignore.
+ A comma will terminate parsing.
- FUNCTION may be an undebuggable function found in minimal symbol table.
+ 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
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. */
-
-static struct symtabs_and_lines
-decode_line_internal (struct linespec_state *self, 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. */
- volatile struct gdb_exception file_exception;
-
- struct cleanup *cleanup = make_cleanup (null_cleanup, NULL);
-
- /* Defaults have defaults. */
-
- initialize_defaults (&self->default_symtab, &self->default_line);
-
- /* See if arg is *PC. */
+/* Parse the linespec in ARG. MATCH_TYPE indicates how function names
+ should be matched. */
- if (**argptr == '*')
+static std::vector<symtab_and_line>
+parse_linespec (linespec_parser *parser, const char *arg,
+ symbol_name_match_type match_type)
+{
+ linespec_token token;
+ struct gdb_exception file_exception;
+
+ /* 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 (parser->completion_tracker == NULL
+ && !is_ada_operator (arg)
+ && strchr (linespec_quote_characters, *arg) != NULL)
{
- do_cleanups (cleanup);
- return decode_indirect (self, argptr);
+ const char *end;
+
+ end = skip_quote_char (arg + 1, *arg);
+ if (end != NULL && is_closing_quote_enclosed (end))
+ {
+ /* Here's the special case. Skip ARG past the initial
+ quote. */
+ ++arg;
+ parser->is_quote_enclosed = 1;
+ }
}
- is_quoted = (strchr (get_gdb_completer_quote_characters (),
- **argptr) != NULL);
+ parser->lexer.saved_arg = arg;
+ parser->lexer.stream = arg;
+ parser->completion_word = arg;
+ parser->complete_what = linespec_complete_what::FUNCTION;
+ PARSER_EXPLICIT (parser)->func_name_match_type = match_type;
+
+ /* Initialize the default symtab and line offset. */
+ initialize_defaults (&PARSER_STATE (parser)->default_symtab,
+ &PARSER_STATE (parser)->default_line);
- if (is_quoted)
+ /* Objective-C shortcut. */
+ if (parser->completion_tracker == NULL)
+ {
+ std::vector<symtab_and_line> values
+ = decode_objc (PARSER_STATE (parser), PARSER_RESULT (parser), arg);
+ if (!values.empty ())
+ return values;
+ }
+ else
{
- end_quote = skip_quoted (*argptr);
- if (*end_quote == '\0')
- is_squote_enclosed = 1;
+ /* "-"/"+" is either an objc selector, or a number. There's
+ nothing to complete the latter to, so just let the caller
+ complete on functions, which finds objc selectors, if there's
+ any. */
+ if ((arg[0] == '-' || arg[0] == '+') && arg[1] == '\0')
+ return {};
}
- /* Check to see if it's a multipart linespec (with colons or
- periods). */
+ /* Start parsing. */
+
+ /* Get the first token. */
+ token = linespec_lexer_consume_token (parser);
+
+ /* It must be either LSTOKEN_STRING or LSTOKEN_NUMBER. */
+ if (token.type == LSTOKEN_STRING && *LS_TOKEN_STOKEN (token).ptr == '$')
+ {
+ /* A NULL entry means to use GLOBAL_DEFAULT_SYMTAB. */
+ if (parser->completion_tracker == NULL)
+ PARSER_RESULT (parser)->file_symtabs->push_back (nullptr);
- /* 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. */
+ /* User specified a convenience variable or history value. */
+ gdb::unique_xmalloc_ptr<char> var = copy_token_string (token);
+ PARSER_EXPLICIT (parser)->line_offset
+ = linespec_parse_variable (PARSER_STATE (parser), var.get ());
+
+ /* If a line_offset wasn't found (VAR is the name of a user
+ variable/function), then skip to normal symbol processing. */
+ if (PARSER_EXPLICIT (parser)->line_offset.sign != LINE_OFFSET_UNKNOWN)
+ {
+ /* Consume this token. */
+ linespec_lexer_consume_token (parser);
- first_half = p = locate_first_half (argptr, &is_quote_enclosed);
+ goto convert_to_sals;
+ }
+ }
+ else if (token.type == LSTOKEN_EOI && parser->completion_tracker != NULL)
+ {
+ /* Let the default linespec_complete_what::FUNCTION kick in. */
+ unexpected_linespec_error (parser);
+ }
+ else if (token.type != LSTOKEN_STRING && token.type != LSTOKEN_NUMBER)
+ {
+ parser->complete_what = linespec_complete_what::NOTHING;
+ unexpected_linespec_error (parser);
+ }
- /* First things first: if ARGPTR starts with a filename, get its
- symtab and strip the filename from ARGPTR.
- Avoid calling symtab_from_filename if we know can,
- it can be expensive. We know we can avoid the call if we see a
- single word (e.g., "break NAME") or if we see a qualified C++
- name ("break QUAL::NAME"). */
+ /* 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 (*p != '\0' && !(p[0] == ':' && p[1] == ':'))
+ if (token.type == LSTOKEN_COLON)
{
- TRY_CATCH (file_exception, RETURN_MASK_ERROR)
+ /* Get the current token again and extract the filename. */
+ token = linespec_lexer_lex_one (parser);
+ gdb::unique_xmalloc_ptr<char> user_filename = copy_token_string (token);
+
+ /* Check if the input is a filename. */
+ try
+ {
+ *PARSER_RESULT (parser)->file_symtabs
+ = symtabs_from_filename (user_filename.get (),
+ PARSER_STATE (parser)->search_pspace);
+ }
+ catch (gdb_exception_error &ex)
{
- self->file_symtabs = symtabs_from_filename (argptr, p,
- is_quote_enclosed,
- &self->user_filename);
+ file_exception = std::move (ex);
}
if (file_exception.reason >= 0)
{
- /* 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);
+ /* Symtabs were found for the file. Record the filename. */
+ PARSER_EXPLICIT (parser)->source_filename = user_filename.release ();
- /* Locate the next "half" of the linespec. */
- first_half = p = locate_first_half (argptr, &is_quote_enclosed);
- }
+ /* Get the next token. */
+ token = linespec_lexer_consume_token (parser);
- if (VEC_empty (symtab_p, self->file_symtabs))
+ /* This is LSTOKEN_COLON; consume it. */
+ linespec_lexer_consume_token (parser);
+ }
+ else
{
/* A NULL entry means to use GLOBAL_DEFAULT_SYMTAB. */
- VEC_safe_push (symtab_p, self->file_symtabs, NULL);
+ PARSER_RESULT (parser)->file_symtabs->push_back (nullptr);
}
}
+ /* If the next token is not EOI, KEYWORD, or COMMA, issue an error. */
+ else if (parser->completion_tracker == NULL
+ && (token.type != LSTOKEN_EOI && token.type != LSTOKEN_KEYWORD
+ && token.type != LSTOKEN_COMMA))
+ {
+ /* 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
{
/* A NULL entry means to use GLOBAL_DEFAULT_SYMTAB. */
- VEC_safe_push (symtab_p, self->file_symtabs, NULL);
+ PARSER_RESULT (parser)->file_symtabs->push_back (nullptr);
+ }
+
+ /* Parse the rest of the linespec. */
+ linespec_parse_basic (parser);
+
+ if (parser->completion_tracker == NULL
+ && PARSER_RESULT (parser)->function_symbols == NULL
+ && PARSER_RESULT (parser)->labels.label_symbols == NULL
+ && PARSER_EXPLICIT (parser)->line_offset.sign == LINE_OFFSET_UNKNOWN
+ && PARSER_RESULT (parser)->minimal_symbols == NULL)
+ {
+ /* The linespec didn't parse. Re-throw the file exception if
+ there was one. */
+ if (file_exception.reason < 0)
+ throw_exception (std::move (file_exception));
+
+ /* Otherwise, the symbol is not found. */
+ symbol_not_found_error (PARSER_EXPLICIT (parser)->function_name,
+ PARSER_EXPLICIT (parser)->source_filename);
}
- /* 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;
+ convert_to_sals:
- /* Check if the symbol could be an Objective-C selector. */
+ /* 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)
+ unexpected_linespec_error (parser);
+ else if (token.type == LSTOKEN_KEYWORD)
+ {
+ /* Setup the completion word past the keyword. Lexing never
+ advances past a keyword automatically, so skip it
+ manually. */
+ parser->completion_word
+ = skip_spaces (skip_to_space (PARSER_STREAM (parser)));
+ parser->complete_what = linespec_complete_what::EXPRESSION;
+ }
- {
- struct symtabs_and_lines values;
+ /* Convert the data in PARSER_RESULT to SALs. */
+ if (parser->completion_tracker == NULL)
+ return convert_linespec_to_sals (PARSER_STATE (parser),
+ PARSER_RESULT (parser));
- values = decode_objc (self, argptr);
- if (values.sals != NULL)
- {
- do_cleanups (cleanup);
- return values;
- }
- }
+ return {};
+}
- /* Does it look like there actually were two parts? */
-
- if (p[0] == ':' || p[0] == '.')
- {
- /* 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] == ':')
- {
- /* We only perform this check for the languages where it might
- make sense. For instance, Ada does not use this type of
- syntax, and trying to apply this logic on an Ada linespec
- may trigger a spurious error (for instance, decode_compound
- does not like expressions such as `ops."<"', which is a
- valid function name in Ada). */
- if (current_language->la_language == language_c
- || current_language->la_language == language_cplus
- || current_language->la_language == language_java)
- {
- struct symtabs_and_lines values;
- volatile struct gdb_exception ex;
- char *saved_argptr = *argptr;
- if (is_quote_enclosed)
- ++saved_arg;
+/* A constructor for linespec_state. */
+
+static void
+linespec_state_constructor (struct linespec_state *self,
+ int flags, const struct language_defn *language,
+ struct program_space *search_pspace,
+ 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->search_pspace = search_pspace;
+ self->default_symtab = default_symtab;
+ self->default_line = default_line;
+ self->canonical = canonical;
+ self->program_space = current_program_space;
+ self->addr_set = htab_create_alloc (10, hash_address_entry, eq_address_entry,
+ xfree, xcalloc, xfree);
+ self->is_linespec = 0;
+}
+
+/* Initialize a new linespec parser. */
+
+linespec_parser::linespec_parser (int flags,
+ const struct language_defn *language,
+ struct program_space *search_pspace,
+ struct symtab *default_symtab,
+ int default_line,
+ struct linespec_result *canonical)
+{
+ lexer.current.type = LSTOKEN_CONSUMED;
+ PARSER_RESULT (this)->file_symtabs = new std::vector<symtab *> ();
+ PARSER_EXPLICIT (this)->func_name_match_type
+ = symbol_name_match_type::WILD;
+ PARSER_EXPLICIT (this)->line_offset.sign = LINE_OFFSET_UNKNOWN;
+ linespec_state_constructor (PARSER_STATE (this), flags, language,
+ search_pspace,
+ default_symtab, default_line, canonical);
+}
+
+/* A destructor for linespec_state. */
+
+static void
+linespec_state_destructor (struct linespec_state *self)
+{
+ htab_delete (self->addr_set);
+ xfree (self->canonical_names);
+}
+
+/* Delete a linespec parser. */
+
+linespec_parser::~linespec_parser ()
+{
+ xfree (PARSER_EXPLICIT (this)->source_filename);
+ xfree (PARSER_EXPLICIT (this)->label_name);
+ xfree (PARSER_EXPLICIT (this)->function_name);
+
+ delete PARSER_RESULT (this)->file_symtabs;
+ delete PARSER_RESULT (this)->function_symbols;
+ delete PARSER_RESULT (this)->minimal_symbols;
+ delete PARSER_RESULT (this)->labels.label_symbols;
+ delete PARSER_RESULT (this)->labels.function_symbols;
+
+ linespec_state_destructor (PARSER_STATE (this));
+}
+
+/* See description in linespec.h. */
- /* Initialize it just to avoid a GCC false warning. */
- memset (&values, 0, sizeof (values));
+void
+linespec_lex_to_end (const char **stringp)
+{
+ linespec_token token;
+ const char *orig;
+
+ if (stringp == NULL || *stringp == NULL)
+ return;
+
+ linespec_parser parser (0, current_language, NULL, NULL, 0, NULL);
+ parser.lexer.saved_arg = *stringp;
+ PARSER_STREAM (&parser) = orig = *stringp;
+
+ do
+ {
+ /* Stop before any comma tokens; we need it to keep it
+ as the next token in the string. */
+ token = linespec_lexer_peek_token (&parser);
+ if (token.type == LSTOKEN_COMMA)
+ break;
+ token = linespec_lexer_consume_token (&parser);
+ }
+ while (token.type != LSTOKEN_EOI && token.type != LSTOKEN_KEYWORD);
+
+ *stringp += PARSER_STREAM (&parser) - orig;
+}
+
+/* See linespec.h. */
+
+void
+linespec_complete_function (completion_tracker &tracker,
+ const char *function,
+ symbol_name_match_type func_match_type,
+ const char *source_filename)
+{
+ complete_symbol_mode mode = complete_symbol_mode::LINESPEC;
+
+ if (source_filename != NULL)
+ {
+ collect_file_symbol_completion_matches (tracker, mode, func_match_type,
+ function, function, source_filename);
+ }
+ else
+ {
+ collect_symbol_completion_matches (tracker, mode, func_match_type,
+ function, function);
+
+ }
+}
+
+/* Helper for complete_linespec to simplify it. SOURCE_FILENAME is
+ only meaningful if COMPONENT is FUNCTION. */
+
+static void
+complete_linespec_component (linespec_parser *parser,
+ completion_tracker &tracker,
+ const char *text,
+ linespec_complete_what component,
+ const char *source_filename)
+{
+ if (component == linespec_complete_what::KEYWORD)
+ {
+ complete_on_enum (tracker, linespec_keywords, text, text);
+ }
+ else if (component == linespec_complete_what::EXPRESSION)
+ {
+ const char *word
+ = advance_to_expression_complete_word_point (tracker, text);
+ complete_expression (tracker, text, word);
+ }
+ else if (component == linespec_complete_what::FUNCTION)
+ {
+ completion_list fn_list;
- TRY_CATCH (ex, RETURN_MASK_ERROR)
- {
- values = decode_compound (self, argptr, saved_arg, p);
- }
- if ((is_quoted || is_squote_enclosed) && **argptr == '\'')
- *argptr = *argptr + 1;
+ symbol_name_match_type match_type
+ = PARSER_EXPLICIT (parser)->func_name_match_type;
+ linespec_complete_function (tracker, text, match_type, source_filename);
+ if (source_filename == NULL)
+ {
+ /* Haven't seen a source component, like in "b
+ file.c:function[TAB]". Maybe this wasn't a function, but
+ a filename instead, like "b file.[TAB]". */
+ fn_list = complete_source_filenames (text);
+ }
- if (ex.reason >= 0)
- {
- do_cleanups (cleanup);
- return values;
- }
+ /* If we only have a single filename completion, append a ':' for
+ the user, since that's the only thing that can usefully follow
+ the filename. */
+ if (fn_list.size () == 1 && !tracker.have_completions ())
+ {
+ char *fn = fn_list[0].release ();
- if (ex.error != NOT_FOUND_ERROR)
- throw_exception (ex);
+ /* If we also need to append a quote char, it needs to be
+ appended before the ':'. Append it now, and make ':' the
+ new "quote" char. */
+ if (tracker.quote_char ())
+ {
+ char quote_char_str[2] = { (char) tracker.quote_char () };
- *argptr = saved_argptr;
+ fn = reconcat (fn, fn, quote_char_str, (char *) NULL);
+ tracker.set_quote_char (':');
}
- }
- 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);
+ else
+ fn = reconcat (fn, fn, ":", (char *) NULL);
+ fn_list[0].reset (fn);
- /* If we did not find a function, re-throw the original
- exception. */
- if (!function_symbols)
- throw_exception (file_exception);
+ /* Tell readline to skip appending a space. */
+ tracker.set_suppress_append_ws (true);
+ }
+ tracker.add_completions (std::move (fn_list));
+ }
+}
- make_cleanup (VEC_cleanup (symbolp), &function_symbols);
- }
+/* Helper for linespec_complete_label. Find labels that match
+ LABEL_NAME in the function symbols listed in the PARSER, and add
+ them to the tracker. */
- /* 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);
- }
+static void
+complete_label (completion_tracker &tracker,
+ linespec_parser *parser,
+ const char *label_name)
+{
+ std::vector<block_symbol> label_function_symbols;
+ std::vector<block_symbol> *labels
+ = find_label_symbols (PARSER_STATE (parser),
+ PARSER_RESULT (parser)->function_symbols,
+ &label_function_symbols,
+ label_name, true);
+
+ if (labels != nullptr)
+ {
+ for (const auto &label : *labels)
+ {
+ char *match = xstrdup (SYMBOL_SEARCH_NAME (label.symbol));
+ tracker.add_completion (gdb::unique_xmalloc_ptr<char> (match));
}
+ delete labels;
}
+}
- /* 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. */
+/* See linespec.h. */
+
+void
+linespec_complete_label (completion_tracker &tracker,
+ const struct language_defn *language,
+ const char *source_filename,
+ const char *function_name,
+ symbol_name_match_type func_name_match_type,
+ const char *label_name)
+{
+ linespec_parser parser (0, language, NULL, NULL, 0, NULL);
- /* If the filename was quoted, we must re-check the quotation. */
+ line_offset unknown_offset = { 0, LINE_OFFSET_UNKNOWN };
- if (end_quote == first_half && *end_quote!= '\0')
+ try
{
- is_quoted = (**argptr
- && strchr (get_gdb_completer_quote_characters (),
- **argptr) != NULL);
- if (is_quoted)
- end_quote = skip_quoted (*argptr);
+ convert_explicit_location_to_linespec (PARSER_STATE (&parser),
+ PARSER_RESULT (&parser),
+ source_filename,
+ function_name,
+ func_name_match_type,
+ NULL, unknown_offset);
+ }
+ catch (const gdb_exception_error &ex)
+ {
+ return;
}
- /* Check whether arg is all digits (and sign). */
+ complete_label (tracker, &parser, label_name);
+}
- q = *argptr;
- if (*q == '-' || *q == '+')
- q++;
- while (*q >= '0' && *q <= '9')
- q++;
+/* See description in linespec.h. */
- if (q != *argptr && (*q == 0 || *q == ' ' || *q == '\t' || *q == ',')
- && function_symbols == NULL)
- {
- struct symtabs_and_lines values;
+void
+linespec_complete (completion_tracker &tracker, const char *text,
+ symbol_name_match_type match_type)
+{
+ const char *orig = text;
- /* We found a token consisting of all digits -- at least one digit. */
- values = decode_all_digits (self, argptr, q);
- do_cleanups (cleanup);
- return values;
- }
+ linespec_parser parser (0, current_language, NULL, NULL, 0, NULL);
+ parser.lexer.saved_arg = text;
+ PARSER_EXPLICIT (&parser)->func_name_match_type = match_type;
+ PARSER_STREAM (&parser) = text;
- /* Arg token is not digits => try it as a variable name
- Find the next token (everything up to end or next whitespace). */
+ parser.completion_tracker = &tracker;
+ PARSER_STATE (&parser)->is_linespec = 1;
- 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)
+ /* Parse as much as possible. parser.completion_word will hold
+ furthest completion point we managed to parse to. */
+ try
{
- p = end_quote;
- if (p[-1] != '\'')
- error (_("Unmatched single quote."));
+ parse_linespec (&parser, text, match_type);
}
- else if (is_objc_method)
+ catch (const gdb_exception_error &except)
{
- /* allow word separators in method names for Obj-C. */
- p = skip_quoted_chars (*argptr, NULL, "");
}
- else
+
+ if (parser.completion_quote_char != '\0'
+ && parser.completion_quote_end != NULL
+ && parser.completion_quote_end[1] == '\0')
{
- p = skip_quoted (*argptr);
- }
+ /* If completing a quoted string with the cursor right at
+ terminating quote char, complete the completion word without
+ interpretation, so that readline advances the cursor one
+ whitespace past the quote, even if there's no match. This
+ makes these cases behave the same:
- /* Keep any important naming information. */
- p = keep_name_info (p, p == saved_arg || is_linespec_boundary (p[-1]));
+ before: "b function()"
+ after: "b function() "
- 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++;
+ before: "b 'function()'"
+ after: "b 'function()' "
+
+ and trusts the user in this case:
+
+ before: "b 'not_loaded_function_yet()'"
+ after: "b 'not_loaded_function_yet()' "
+ */
+ parser.complete_what = linespec_complete_what::NOTHING;
+ parser.completion_quote_char = '\0';
+
+ gdb::unique_xmalloc_ptr<char> text_copy
+ (xstrdup (parser.completion_word));
+ tracker.add_completion (std::move (text_copy));
}
- 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. */
+ tracker.set_quote_char (parser.completion_quote_char);
- if (*copy == '$' && function_symbols == NULL)
+ if (parser.complete_what == linespec_complete_what::LABEL)
{
- struct symtabs_and_lines values;
+ parser.complete_what = linespec_complete_what::NOTHING;
- values = decode_dollar (self, copy);
- do_cleanups (cleanup);
- return values;
- }
+ const char *func_name = PARSER_EXPLICIT (&parser)->function_name;
+
+ std::vector<block_symbol> function_symbols;
+ std::vector<bound_minimal_symbol> minimal_symbols;
+ find_linespec_symbols (PARSER_STATE (&parser),
+ PARSER_RESULT (&parser)->file_symtabs,
+ func_name, match_type,
+ &function_symbols, &minimal_symbols);
- /* Try the token as a label, but only if no file was specified,
- because we can only really find labels in the current scope. */
+ PARSER_RESULT (&parser)->function_symbols
+ = new std::vector<block_symbol> (std::move (function_symbols));
+ PARSER_RESULT (&parser)->minimal_symbols
+ = new std::vector<bound_minimal_symbol> (std::move (minimal_symbols));
- if (VEC_length (symtab_p, self->file_symtabs) == 1
- && VEC_index (symtab_p, self->file_symtabs, 0) == NULL)
+ complete_label (tracker, &parser, parser.completion_word);
+ }
+ else if (parser.complete_what == linespec_complete_what::FUNCTION)
{
- struct symtabs_and_lines label_result;
- if (decode_label (self, function_symbols, copy, &label_result))
+ /* While parsing/lexing, we didn't know whether the completion
+ word completes to a unique function/source name already or
+ not.
+
+ E.g.:
+ "b function() <tab>"
+ may need to complete either to:
+ "b function() const"
+ or to:
+ "b function() if/thread/task"
+
+ Or, this:
+ "b foo t"
+ may need to complete either to:
+ "b foo template_fun<T>()"
+ with "foo" being the template function's return type, or to:
+ "b foo thread/task"
+
+ Or, this:
+ "b file<TAB>"
+ may need to complete either to a source file name:
+ "b file.c"
+ or this, also a filename, but a unique completion:
+ "b file.c:"
+ or to a function name:
+ "b file_function"
+
+ Address that by completing assuming source or function, and
+ seeing if we find a completion that matches exactly the
+ completion word. If so, then it must be a function (see note
+ below) and we advance the completion word to the end of input
+ and switch to KEYWORD completion mode.
+
+ Note: if we find a unique completion for a source filename,
+ then it won't match the completion word, because the LCD will
+ contain a trailing ':'. And if we're completing at or after
+ the ':', then complete_linespec_component won't try to
+ complete on source filenames. */
+
+ const char *word = parser.completion_word;
+
+ complete_linespec_component (&parser, tracker,
+ parser.completion_word,
+ linespec_complete_what::FUNCTION,
+ PARSER_EXPLICIT (&parser)->source_filename);
+
+ parser.complete_what = linespec_complete_what::NOTHING;
+
+ if (tracker.quote_char ())
{
- do_cleanups (cleanup);
- return label_result;
+ /* The function/file name was not close-quoted, so this
+ can't be a keyword. Note: complete_linespec_component
+ may have swapped the original quote char for ':' when we
+ get here, but that still indicates the same. */
}
- }
+ else if (!tracker.have_completions ())
+ {
+ size_t key_start;
+ size_t wordlen = strlen (parser.completion_word);
- if (function_symbols)
- throw_exception (file_exception);
+ key_start
+ = string_find_incomplete_keyword_at_end (linespec_keywords,
+ parser.completion_word,
+ wordlen);
- /* Look up that token as a variable.
- If file specified, use that file's per-file block to start with. */
+ if (key_start != -1
+ || (wordlen > 0
+ && parser.completion_word[wordlen - 1] == ' '))
+ {
+ parser.completion_word += key_start;
+ parser.complete_what = linespec_complete_what::KEYWORD;
+ }
+ }
+ else if (tracker.completes_to_completion_word (word))
+ {
+ /* Skip the function and complete on keywords. */
+ parser.completion_word += strlen (word);
+ parser.complete_what = linespec_complete_what::KEYWORD;
+ tracker.discard_completions ();
+ }
+ }
- {
- struct symtabs_and_lines values;
+ tracker.advance_custom_word_point_by (parser.completion_word - orig);
+
+ complete_linespec_component (&parser, tracker,
+ parser.completion_word,
+ parser.complete_what,
+ PARSER_EXPLICIT (&parser)->source_filename);
+
+ /* If we're past the "filename:function:label:offset" linespec, and
+ didn't find any match, then assume the user might want to create
+ a pending breakpoint anyway and offer the keyword
+ completions. */
+ if (!parser.completion_quote_char
+ && (parser.complete_what == linespec_complete_what::FUNCTION
+ || parser.complete_what == linespec_complete_what::LABEL
+ || parser.complete_what == linespec_complete_what::NOTHING)
+ && !tracker.have_completions ())
+ {
+ const char *end
+ = parser.completion_word + strlen (parser.completion_word);
- values = decode_variable (self, copy);
- do_cleanups (cleanup);
- return values;
- }
+ if (end > orig && end[-1] == ' ')
+ {
+ tracker.advance_custom_word_point_by (end - parser.completion_word);
+
+ complete_linespec_component (&parser, tracker, end,
+ linespec_complete_what::KEYWORD,
+ NULL);
+ }
+ }
}
-/* A constructor for linespec_state. */
+/* A helper function for decode_line_full and decode_line_1 to
+ turn LOCATION into std::vector<symtab_and_line>. */
-static void
-linespec_state_constructor (struct linespec_state *self,
- int flags,
- struct symtab *default_symtab,
- int default_line,
- struct linespec_result *canonical)
+static std::vector<symtab_and_line>
+event_location_to_sals (linespec_parser *parser,
+ const struct event_location *location)
{
- memset (self, 0, sizeof (*self));
- self->funfirstline = (flags & DECODE_LINE_FUNFIRSTLINE) ? 1 : 0;
- self->list_mode = (flags & DECODE_LINE_LIST_MODE) ? 1 : 0;
- self->default_symtab = default_symtab;
- self->default_line = default_line;
- self->canonical = canonical;
- self->program_space = current_program_space;
- self->addr_set = htab_create_alloc (10, hash_address_entry, eq_address_entry,
- xfree, xcalloc, xfree);
-}
+ std::vector<symtab_and_line> result;
-/* A destructor for linespec_state. */
+ switch (event_location_type (location))
+ {
+ case LINESPEC_LOCATION:
+ {
+ PARSER_STATE (parser)->is_linespec = 1;
+ try
+ {
+ const linespec_location *ls = get_linespec_location (location);
+ result = parse_linespec (parser,
+ ls->spec_string, ls->match_type);
+ }
+ catch (const gdb_exception_error &except)
+ {
+ throw;
+ }
+ }
+ break;
-static void
-linespec_state_destructor (void *arg)
-{
- struct linespec_state *self = arg;
+ case ADDRESS_LOCATION:
+ {
+ const char *addr_string = get_address_string_location (location);
+ CORE_ADDR addr = get_address_location (location);
- xfree (self->user_filename);
- xfree (self->user_function);
- VEC_free (symtab_p, self->file_symtabs);
- htab_delete (self->addr_set);
+ if (addr_string != NULL)
+ {
+ addr = linespec_expression_to_pc (&addr_string);
+ if (PARSER_STATE (parser)->canonical != NULL)
+ PARSER_STATE (parser)->canonical->location
+ = copy_event_location (location);
+ }
+
+ result = convert_address_location_to_sals (PARSER_STATE (parser),
+ addr);
+ }
+ break;
+
+ case EXPLICIT_LOCATION:
+ {
+ const struct explicit_location *explicit_loc;
+
+ explicit_loc = get_explicit_location_const (location);
+ result = convert_explicit_location_to_sals (PARSER_STATE (parser),
+ PARSER_RESULT (parser),
+ explicit_loc);
+ }
+ break;
+
+ case PROBE_LOCATION:
+ /* Probes are handled by their own decoders. */
+ gdb_assert_not_reached ("attempt to decode probe location");
+ break;
+
+ default:
+ gdb_assert_not_reached ("unhandled event location type");
+ }
+
+ return result;
}
/* See linespec.h. */
void
-decode_line_full (char **argptr, int flags,
+decode_line_full (const struct event_location *location, int flags,
+ struct program_space *search_pspace,
struct symtab *default_symtab,
int default_line, struct linespec_result *canonical,
const char *select_mode,
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;
+ std::vector<const char *> filters;
+ struct linespec_state *state;
gdb_assert (canonical != NULL);
/* The filter only makes sense for 'all'. */
|| 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);
- save_current_program_space ();
+ linespec_parser parser (flags, current_language,
+ search_pspace, default_symtab,
+ default_line, canonical);
+
+ scoped_restore_current_program_space restore_pspace;
- result = decode_line_internal (&state, argptr);
+ std::vector<symtab_and_line> result = event_location_to_sals (&parser,
+ location);
+ state = PARSER_STATE (&parser);
- gdb_assert (result.nelts == 1 || canonical->pre_expanded);
- gdb_assert (canonical->addr_string != NULL);
+ gdb_assert (result.size () == 1 || canonical->pre_expanded);
canonical->pre_expanded = 1;
- /* Fill in the missing canonical names. */
- if (result.nelts > 0)
+ /* Arrange for allocated canonical names to be freed. */
+ std::vector<gdb::unique_xmalloc_ptr<char>> hold_names;
+ for (int i = 0; i < result.size (); ++i)
{
- int i;
-
- 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]);
- }
+ gdb_assert (state->canonical_names[i].suffix != NULL);
+ hold_names.emplace_back (state->canonical_names[i].suffix);
}
if (select_mode == NULL)
{
- if (ui_out_is_mi_like_p (interp_ui_out (top_level_interpreter ())))
+ if (top_level_interpreter ()->interp_ui_out ()->is_mi_like_p ())
select_mode = multiple_symbols_all;
else
select_mode = multiple_symbols_select_mode ();
{
if (filter != NULL)
{
- make_cleanup (VEC_cleanup (const_char_ptr), &filters);
- VEC_safe_push (const_char_ptr, filters, filter);
- filter_results (&state, &result, filters);
+ filters.push_back (filter);
+ 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);
-
- do_cleanups (cleanups);
+ decode_line_2 (state, &result, select_mode);
}
-struct symtabs_and_lines
-decode_line_1 (char **argptr, int flags,
+/* See linespec.h. */
+
+std::vector<symtab_and_line>
+decode_line_1 (const struct event_location *location, int flags,
+ struct program_space *search_pspace,
struct symtab *default_symtab,
int default_line)
{
- struct symtabs_and_lines result;
- struct linespec_state state;
- struct cleanup *cleanups;
+ linespec_parser parser (flags, current_language,
+ search_pspace, default_symtab,
+ default_line, NULL);
- linespec_state_constructor (&state, flags,
- default_symtab, default_line, NULL);
- cleanups = make_cleanup (linespec_state_destructor, &state);
- save_current_program_space ();
+ scoped_restore_current_program_space restore_pspace;
- result = decode_line_internal (&state, argptr);
- do_cleanups (cleanups);
- return result;
+ return event_location_to_sals (&parser, location);
+}
+
+/* See linespec.h. */
+
+std::vector<symtab_and_line>
+decode_line_with_current_source (const char *string, int flags)
+{
+ 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! */
+ symtab_and_line cursal = get_current_source_symtab_and_line ();
+
+ event_location_up location = string_to_event_location (&string,
+ current_language);
+ std::vector<symtab_and_line> sals
+ = decode_line_1 (location.get (), flags, NULL, cursal.symtab, cursal.line);
+
+ if (*string)
+ error (_("Junk at end of line specification: %s"), string);
+
+ return sals;
+}
+
+/* See linespec.h. */
+
+std::vector<symtab_and_line>
+decode_line_with_last_displayed (const char *string, int flags)
+{
+ if (string == 0)
+ error (_("Empty line specification."));
+
+ event_location_up location = string_to_event_location (&string,
+ current_language);
+ std::vector<symtab_and_line> sals
+ = (last_displayed_sal_is_valid ()
+ ? decode_line_1 (location.get (), flags, NULL,
+ get_last_displayed_symtab (),
+ get_last_displayed_line ())
+ : decode_line_1 (location.get (), flags, NULL, NULL, 0));
+
+ if (*string)
+ error (_("Junk at end of line specification: %s"), string);
+
+ return sals;
}
\f
\f
-/* 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)
+CORE_ADDR
+linespec_expression_to_pc (const 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;
-}
-
-\f
-
-/* 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;
-
- /* Check if the linespec starts with an Ada operator (such as "+",
- or ">", for instance). */
- p = *argptr;
- if (p[0] == '"'
- && current_language->la_language == language_ada)
- {
- const struct ada_opname_map *op;
-
- for (op = ada_opname_table; op->encoded != NULL; op++)
- if (strncmp (op->decoded, p, strlen (op->decoded)) == 0)
- break;
- if (op->encoded != NULL)
- {
- *is_quote_enclosed = 0;
- return p + strlen (op->decoded);
- }
- }
-
- /* 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));
}
\f
than one method that could represent the selector, then use some of
the existing C++ code to let the user choose one. */
-static struct symtabs_and_lines
-decode_objc (struct linespec_state *self, char **argptr)
+static std::vector<symtab_and_line>
+decode_objc (struct linespec_state *self, linespec_p ls, const char *arg)
{
struct collect_info info;
- VEC (const_char_ptr) *symbol_names = NULL;
- char *new_argptr;
- struct cleanup *cleanup = make_cleanup (VEC_cleanup (const_char_ptr),
- &symbol_names);
+ std::vector<const char *> symbol_names;
+ const char *new_argptr;
info.state = self;
- info.result.sals = NULL;
- info.result.nelts = 0;
-
- new_argptr = find_imps (*argptr, &symbol_names);
- if (VEC_empty (const_char_ptr, symbol_names))
- {
- do_cleanups (cleanup);
- return info.result;
- }
-
- add_all_symbol_names_from_pspace (&info, NULL, symbol_names);
-
- if (info.result.nelts > 0)
- {
- char *saved_arg;
-
- saved_arg = alloca (new_argptr - *argptr + 1);
- memcpy (saved_arg, *argptr, new_argptr - *argptr);
- saved_arg[new_argptr - *argptr] = '\0';
-
- 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);
- }
- }
-
- *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.
+ std::vector<symtab *> symtabs;
+ symtabs.push_back (nullptr);
- 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.
+ info.file_symtabs = &symtabs;
- 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. */
+ std::vector<block_symbol> symbols;
+ info.result.symbols = &symbols;
+ std::vector<bound_minimal_symbol> minimal_symbols;
+ info.result.minimal_symbols = &minimal_symbols;
- p2 = p; /* Save for restart. */
+ new_argptr = find_imps (arg, &symbol_names);
+ if (symbol_names.empty ())
+ return {};
- /* 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". */
+ add_all_symbol_names_from_pspace (&info, NULL, symbol_names,
+ FUNCTIONS_DOMAIN);
- /* In the loop below, with these strings, we'll make 2 passes, each
- is marked in comments. */
-
- while (1)
+ std::vector<symtab_and_line> values;
+ if (!symbols.empty () || !minimal_symbols.empty ())
{
- static char *break_characters = " \t(";
-
- /* Move pointer up to next possible class/namespace token. */
+ char *saved_arg;
- p = p2 + 1; /* Restart with old value +1. */
+ saved_arg = (char *) alloca (new_argptr - arg + 1);
+ memcpy (saved_arg, arg, new_argptr - arg);
+ saved_arg[new_argptr - arg] = '\0';
- /* 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" */
+ ls->explicit_loc.function_name = xstrdup (saved_arg);
+ ls->function_symbols
+ = new std::vector<block_symbol> (std::move (symbols));
+ ls->minimal_symbols
+ = new std::vector<bound_minimal_symbol> (std::move (minimal_symbols));
+ values = convert_linespec_to_sals (self, ls);
- /* Move pointer ahead to next double-colon. */
- while (*p
- && strchr (break_characters, *p) == NULL
- && strchr (get_gdb_completer_quote_characters (), *p) == NULL)
+ if (self->canonical)
{
- 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". */
+ std::string holder;
+ const char *str;
- /* 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);
+ self->canonical->pre_expanded = 1;
- /* 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)
+ if (ls->explicit_loc.source_filename)
{
- /* The above loop has already swallowed "operator". */
- p += cp_validate_operator (p - 8) - 8;
+ holder = string_printf ("%s:%s",
+ ls->explicit_loc.source_filename,
+ saved_arg);
+ str = holder.c_str ();
}
+ else
+ str = saved_arg;
- /* Keep any important naming information. */
- p = keep_name_info (p, 1);
+ self->canonical->location
+ = new_linespec_location (&str, symbol_name_match_type::FULL);
}
+ }
- /* 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. */
+ return values;
+}
+namespace {
- /* 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. */
+/* A function object that serves as symbol_found_callback_ftype
+ callback for iterate_over_symbols. This is used by
+ lookup_prefix_sym to collect type symbols. */
+class decode_compound_collector
+{
+public:
+ decode_compound_collector ()
+ {
+ m_unique_syms = htab_create_alloc (1, htab_hash_pointer,
+ htab_eq_pointer, NULL,
+ xcalloc, xfree);
+ }
- throw_error (NOT_FOUND_ERROR, _("see caller, this text doesn't matter"));
-}
+ ~decode_compound_collector ()
+ {
+ if (m_unique_syms != NULL)
+ htab_delete (m_unique_syms);
+ }
-/* An instance of this type is used when collecting prefix symbols for
- decode_compound. */
+ /* Return all symbols collected. */
+ std::vector<block_symbol> release_symbols ()
+ {
+ return std::move (m_symbols);
+ }
-struct decode_compound_collector
-{
- /* The result vector. */
- VEC (symbolp) *symbols;
+ /* Callable as a symbol_found_callback_ftype callback. */
+ bool operator () (block_symbol *bsym);
+private:
/* A hash table of all symbols we found. We use this to avoid
adding any symbol more than once. */
- htab_t unique_syms;
-};
+ htab_t m_unique_syms;
-/* A callback for iterate_over_symbols that is used by
- lookup_prefix_sym to collect type symbols. */
+ /* The result vector. */
+ std::vector<block_symbol> m_symbols;
+};
-static int
-collect_one_symbol (struct symbol *sym, void *d)
+bool
+decode_compound_collector::operator () (block_symbol *bsym)
{
- struct decode_compound_collector *collector = d;
void **slot;
struct type *t;
+ struct symbol *sym = bsym->symbol;
if (SYMBOL_CLASS (sym) != LOC_TYPEDEF)
- return 1; /* Continue iterating. */
+ return true; /* Continue iterating. */
t = SYMBOL_TYPE (sym);
- CHECK_TYPEDEF (t);
+ t = check_typedef (t);
if (TYPE_CODE (t) != TYPE_CODE_STRUCT
&& TYPE_CODE (t) != TYPE_CODE_UNION
&& TYPE_CODE (t) != TYPE_CODE_NAMESPACE)
- return 1; /* Continue iterating. */
+ return true; /* Continue iterating. */
- slot = htab_find_slot (collector->unique_syms, sym, INSERT);
+ slot = htab_find_slot (m_unique_syms, sym, INSERT);
if (!*slot)
{
*slot = sym;
- VEC_safe_push (symbolp, collector->symbols, sym);
+ m_symbols.push_back (*bsym);
}
- return 1; /* Continue iterating. */
+ return true; /* 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". */
-
-static VEC (symbolp) *
-lookup_prefix_sym (char **argptr, char *p, VEC (symtab_p) *file_symtabs,
- 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;
+} // namespace
- /* At this point p1->"::inA::fun", p->"inA::fun" copy->"AAA",
- argptr->"inA::fun". */
+/* Return any symbols corresponding to CLASS_NAME in FILE_SYMTABS. */
- collector.symbols = NULL;
- make_cleanup (VEC_cleanup (symbolp), &collector.symbols);
+static std::vector<block_symbol>
+lookup_prefix_sym (struct linespec_state *state,
+ std::vector<symtab *> *file_symtabs,
+ const char *class_name)
+{
+ decode_compound_collector collector;
- collector.unique_syms = htab_create_alloc (1, htab_hash_pointer,
- htab_eq_pointer, NULL,
- xcalloc, xfree);
- cleanup = make_cleanup_htab_delete (collector.unique_syms);
+ lookup_name_info lookup_name (class_name, symbol_name_match_type::FULL);
- for (ix = 0; VEC_iterate (symtab_p, file_symtabs, ix, elt); ++ix)
+ for (const auto &elt : *file_symtabs)
{
- if (elt == NULL)
+ if (elt == nullptr)
{
- iterate_over_all_matching_symtabs (copy, STRUCT_DOMAIN,
- collect_one_symbol, &collector,
- NULL);
- iterate_over_all_matching_symtabs (copy, VAR_DOMAIN,
- collect_one_symbol, &collector,
- NULL);
+ iterate_over_all_matching_symtabs (state, lookup_name,
+ STRUCT_DOMAIN, ALL_DOMAIN,
+ NULL, false, collector);
+ iterate_over_all_matching_symtabs (state, lookup_name,
+ VAR_DOMAIN, ALL_DOMAIN,
+ NULL, false, collector);
}
else
{
- struct block *search_block;
-
/* Program spaces that are executing startup should have
been filtered out earlier. */
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,
- collect_one_symbol, &collector);
- LA_ITERATE_OVER_SYMBOLS (search_block, copy, VAR_DOMAIN,
- collect_one_symbol, &collector);
+ iterate_over_file_blocks (elt, lookup_name, STRUCT_DOMAIN, collector);
+ iterate_over_file_blocks (elt, lookup_name, VAR_DOMAIN, collector);
}
}
- do_cleanups (cleanup);
- discard_cleanups (outer);
- return collector.symbols;
+ return collector.release_symbols ();
}
-/* A qsort comparison function for symbols. The resulting order does
+/* A std::sort 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)
+static bool
+compare_symbols (const block_symbol &a, const block_symbol &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));
+ uia = (uintptr_t) SYMTAB_PSPACE (symbol_symtab (a.symbol));
+ uib = (uintptr_t) SYMTAB_PSPACE (symbol_symtab (b.symbol));
if (uia < uib)
- return -1;
+ return true;
if (uia > uib)
- return 1;
+ return false;
+
+ uia = (uintptr_t) a.symbol;
+ uib = (uintptr_t) b.symbol;
+
+ if (uia < uib)
+ return true;
+
+ return false;
+}
- uia = (uintptr_t) *sa;
- uib = (uintptr_t) *sb;
+/* Like compare_symbols but for minimal symbols. */
+
+static bool
+compare_msymbols (const bound_minimal_symbol &a, const bound_minimal_symbol &b)
+{
+ uintptr_t uia, uib;
+
+ uia = (uintptr_t) a.objfile->pspace;
+ uib = (uintptr_t) a.objfile->pspace;
if (uia < uib)
- return -1;
+ return true;
if (uia > uib)
- return 1;
+ return false;
- return 0;
+ uia = (uintptr_t) a.minsym;
+ uib = (uintptr_t) b.minsym;
+
+ if (uia < uib)
+ return true;
+
+ return false;
}
/* Look for all the matching instances of each symbol in NAMES. Only
static void
add_all_symbol_names_from_pspace (struct collect_info *info,
struct program_space *pspace,
- VEC (const_char_ptr) *names)
+ const std::vector<const char *> &names,
+ enum search_domain search_domain)
{
- int ix;
- const char *iter;
-
- for (ix = 0; VEC_iterate (const_char_ptr, names, ix, iter); ++ix)
- add_matching_symbols_to_info (iter, info, pspace);
+ for (const char *iter : names)
+ add_matching_symbols_to_info (iter,
+ symbol_name_match_type::FULL,
+ search_domain, info, pspace);
}
static void
-find_superclass_methods (VEC (typep) *superclasses,
- const char *name,
- VEC (const_char_ptr) **result_names)
+find_superclass_methods (std::vector<struct type *> &&superclasses,
+ const char *name, enum language name_lang,
+ std::vector<const char *> *result_names)
{
- int old_len = VEC_length (const_char_ptr, *result_names);
- VEC (typep) *iter_classes;
- struct cleanup *cleanup = make_cleanup (null_cleanup, NULL);
+ size_t old_len = result_names->size ();
- iter_classes = superclasses;
while (1)
{
- VEC (typep) *new_supers = NULL;
- int ix;
- struct type *t;
+ std::vector<struct type *> new_supers;
- make_cleanup (VEC_cleanup (typep), &new_supers);
- for (ix = 0; VEC_iterate (typep, iter_classes, ix, t); ++ix)
- find_methods (t, name, result_names, &new_supers);
+ for (type *t : superclasses)
+ find_methods (t, name_lang, name, result_names, &new_supers);
- if (VEC_length (const_char_ptr, *result_names) != old_len
- || VEC_empty (typep, new_supers))
+ if (result_names->size () != old_len || new_supers.empty ())
break;
- iter_classes = new_supers;
+ superclasses = std::move (new_supers);
}
-
- 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, std::vector<symtab *> *file_symtabs,
+ const char *class_name, const char *method_name,
+ std::vector<block_symbol> *sym_classes,
+ std::vector<block_symbol> *symbols,
+ std::vector<bound_minimal_symbol> *minsyms)
{
- char *canon;
- struct symbol *sym;
- struct cleanup *cleanup = make_cleanup (null_cleanup, NULL);
- int ix;
- int last_result_len;
- VEC (typep) *superclass_vec;
- VEC (const_char_ptr) *result_names;
+ size_t last_result_len;
+ std::vector<struct type *> superclass_vec;
+ std::vector<const char *> 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. */
- qsort (VEC_address (symbolp, sym_classes),
- VEC_length (symbolp, sym_classes),
- sizeof (symbolp),
- compare_symbols);
+ std::sort (sym_classes->begin (), sym_classes->end (),
+ compare_symbols);
info.state = self;
- info.result.sals = NULL;
- info.result.nelts = 0;
+ info.file_symtabs = file_symtabs;
+ info.result.symbols = symbols;
+ info.result.minimal_symbols = minsyms;
/* 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
those names. This loop is written in a somewhat funny way
because we collect data across the program space before deciding
what to do. */
- superclass_vec = NULL;
- make_cleanup (VEC_cleanup (typep), &superclass_vec);
- result_names = NULL;
- make_cleanup (VEC_cleanup (const_char_ptr), &result_names);
last_result_len = 0;
- for (ix = 0; VEC_iterate (symbolp, sym_classes, ix, sym); ++ix)
+ unsigned int ix = 0;
+ for (const auto &elt : *sym_classes)
{
struct type *t;
struct program_space *pspace;
+ struct symbol *sym = elt.symbol;
/* Program spaces that are executing startup should have
been filtered out earlier. */
- gdb_assert (!SYMTAB_PSPACE (SYMBOL_SYMTAB (sym))->executing_startup);
- pspace = SYMTAB_PSPACE (SYMBOL_SYMTAB (sym));
+ pspace = SYMTAB_PSPACE (symbol_symtab (sym));
+ gdb_assert (!pspace->executing_startup);
set_current_program_space (pspace);
t = check_typedef (SYMBOL_TYPE (sym));
- find_methods (t, copy, &result_names, &superclass_vec);
+ find_methods (t, SYMBOL_LANGUAGE (sym),
+ 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. */
- if (ix == VEC_length (symbolp, sym_classes) - 1
+ if (ix == sym_classes->size () - 1
|| (pspace
- != SYMTAB_PSPACE (SYMBOL_SYMTAB (VEC_index (symbolp, sym_classes,
- ix + 1)))))
+ != SYMTAB_PSPACE (symbol_symtab (sym_classes->at (ix + 1).symbol))))
{
/* 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);
+ if (result_names.size () == last_result_len)
+ find_superclass_methods (std::move (superclass_vec), method_name,
+ SYMBOL_LANGUAGE (sym), &result_names);
/* We have a list of candidate symbol names, so now we
iterate over the symbol tables looking for all
matches in this pspace. */
- add_all_symbol_names_from_pspace (&info, pspace, result_names);
+ add_all_symbol_names_from_pspace (&info, pspace, result_names,
+ FUNCTIONS_DOMAIN);
- VEC_truncate (typep, superclass_vec, 0);
- last_result_len = VEC_length (const_char_ptr, result_names);
+ superclass_vec.clear ();
+ last_result_len = result_names.size ();
+ ++ix;
}
}
- 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, saved_arg);
- else
- self->canonical->addr_string = xstrdup (saved_arg);
- }
-
- do_cleanups (cleanup);
-
- return info.result;
- }
+ if (!symbols->empty () || !minsyms->empty ())
+ 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"));
}
\f
-/* This object is used when collecting all matching symtabs. */
-
-struct symtab_collector
-{
- /* The result vector of symtabs. */
- VEC (symtab_p) *symtabs;
-
- /* This is used to ensure the symtabs are unique. */
- htab_t symtab_table;
-};
+namespace {
-/* Callback for iterate_over_symtabs. */
+/* This function object is a callback for iterate_over_symtabs, used
+ when collecting all matching symtabs. */
-static int
-add_symtabs_to_list (struct symtab *symtab, void *d)
+class symtab_collector
{
- struct symtab_collector *data = d;
- void **slot;
-
- slot = htab_find_slot (data->symtab_table, symtab, INSERT);
- if (!*slot)
- {
- *slot = symtab;
- VEC_safe_push (symtab_p, data->symtabs, symtab);
- }
-
- return 0;
-}
-
-/* Given a file name, return a VEC of all matching symtabs. */
+public:
+ symtab_collector ()
+ {
+ m_symtab_table = htab_create (1, htab_hash_pointer, htab_eq_pointer,
+ NULL);
+ }
-static VEC (symtab_p) *
-collect_symtabs_from_filename (const char *file)
-{
- struct symtab_collector collector;
- struct cleanup *cleanups;
- struct program_space *pspace;
+ ~symtab_collector ()
+ {
+ if (m_symtab_table != NULL)
+ htab_delete (m_symtab_table);
+ }
- collector.symtabs = NULL;
- collector.symtab_table = htab_create (1, htab_hash_pointer, htab_eq_pointer,
- NULL);
- cleanups = make_cleanup_htab_delete (collector.symtab_table);
+ /* Callable as a symbol_found_callback_ftype callback. */
+ bool operator () (symtab *sym);
- /* Find that file's data. */
- ALL_PSPACES (pspace)
+ /* Return an rvalue reference to the collected symtabs. */
+ std::vector<symtab *> &&release_symtabs ()
{
- if (pspace->executing_startup)
- continue;
-
- set_current_program_space (pspace);
- iterate_over_symtabs (file, add_symtabs_to_list, &collector);
+ return std::move (m_symtabs);
}
- do_cleanups (cleanups);
- return collector.symtabs;
-}
+private:
+ /* The result vector of symtabs. */
+ std::vector<symtab *> m_symtabs;
-/* Return all the symtabs associated to the filename given by the
- substring of *ARGPTR ending at P, and advance ARGPTR past that
- filename. */
+ /* This is used to ensure the symtabs are unique. */
+ htab_t m_symtab_table;
+};
-static VEC (symtab_p) *
-symtabs_from_filename (char **argptr, char *p, int is_quote_enclosed,
- char **user_filename)
+bool
+symtab_collector::operator () (struct symtab *symtab)
{
- 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);
+ void **slot;
- if (VEC_empty (symtab_p, result))
+ slot = htab_find_slot (m_symtab_table, symtab, INSERT);
+ if (!*slot)
{
- 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);
+ *slot = symtab;
+ m_symtabs.push_back (symtab);
}
- /* 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;
+ return false;
}
-/* 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; /* Continue iterating. */
-}
+} // namespace
-/* Look up a function symbol in *ARGPTR. If found, advance *ARGPTR
- and return the symbol. If not found, return NULL. */
+/* Given a file name, return a list of all matching symtabs. If
+ SEARCH_PSPACE is not NULL, the search is restricted to just that
+ program space. */
-static VEC (symbolp) *
-find_function_symbols (char **argptr, char *p, int is_quote_enclosed,
- char **user_function)
+static std::vector<symtab *>
+collect_symtabs_from_filename (const char *file,
+ struct program_space *search_pspace)
{
- char *p1;
- char *copy;
- VEC (symbolp) *result = NULL;
+ symtab_collector collector;
- 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;
+ /* Find that file's data. */
+ if (search_pspace == NULL)
+ {
+ struct program_space *pspace;
- iterate_over_all_matching_symtabs (copy, VAR_DOMAIN,
- collect_function_symbols, &result, NULL);
+ ALL_PSPACES (pspace)
+ {
+ if (pspace->executing_startup)
+ continue;
- if (VEC_empty (symbolp, result))
- VEC_free (symbolp, result);
+ set_current_program_space (pspace);
+ iterate_over_symtabs (file, collector);
+ }
+ }
else
{
- /* Discard the file name from the arg. */
- *argptr = skip_spaces (p1 + 1);
+ set_current_program_space (search_pspace);
+ iterate_over_symtabs (file, collector);
}
- return result;
+ return collector.release_symtabs ();
}
-\f
-
-/* A helper for decode_all_digits that handles the 'list_mode' case. */
+/* Return all the symtabs associated to the FILENAME. If SEARCH_PSPACE is
+ not NULL, the search is restricted to just that program space. */
-static void
-decode_digits_list_mode (struct linespec_state *self,
- struct symtabs_and_lines *values,
- struct symtab_and_line val)
+static std::vector<symtab *>
+symtabs_from_filename (const char *filename,
+ struct program_space *search_pspace)
{
- int ix;
- struct symtab *elt;
-
- gdb_assert (self->list_mode);
+ std::vector<symtab *> result
+ = collect_symtabs_from_filename (filename, search_pspace);
- for (ix = 0; VEC_iterate (symtab_p, self->file_symtabs, ix, elt); ++ix)
+ if (result.empty ())
{
- /* 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);
+ if (!have_full_symbols () && !have_partial_symbols ())
+ throw_error (NOT_FOUND_ERROR,
+ _("No symbol table is loaded. "
+ "Use the \"file\" command."));
+ source_file_not_found_error (filename);
}
-}
-
-/* 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);
- }
+ return result;
}
-/* 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. */
+/* See symtab.h. */
-static struct symtabs_and_lines
-decode_all_digits (struct linespec_state *self,
- char **argptr,
- char *q)
+void
+symbol_searcher::find_all_symbols (const std::string &name,
+ const struct language_defn *language,
+ enum search_domain search_domain,
+ std::vector<symtab *> *search_symtabs,
+ struct program_space *search_pspace)
{
- 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);
- }
+ symbol_searcher_collect_info info;
+ struct linespec_state state;
- 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);
- }
+ memset (&state, 0, sizeof (state));
+ state.language = language;
+ info.state = &state;
- if (self->canonical)
+ info.result.symbols = &m_symbols;
+ info.result.minimal_symbols = &m_minimal_symbols;
+ std::vector<symtab *> all_symtabs;
+ if (search_symtabs == nullptr)
{
- 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);
+ all_symtabs.push_back (nullptr);
+ search_symtabs = &all_symtabs;
}
+ info.file_symtabs = search_symtabs;
- return values;
+ add_matching_symbols_to_info (name.c_str (), symbol_name_match_type::WILD,
+ search_domain, &info, search_pspace);
}
-\f
-
-/* 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,
+ std::vector<symtab *> *file_symtabs, const char *name,
+ symbol_name_match_type name_match_type,
+ std::vector<block_symbol> *symbols,
+ std::vector<bound_minimal_symbol> *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;
-
- 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;
-
- 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);
- }
+ struct collect_info info;
+ std::vector<const char *> symbol_names;
+
+ info.state = state;
+ info.result.symbols = symbols;
+ info.result.minimal_symbols = minsyms;
+ info.file_symtabs = file_symtabs;
+
+ /* Try NAME as an Objective-C selector. */
+ find_imps (name, &symbol_names);
+ if (!symbol_names.empty ())
+ add_all_symbol_names_from_pspace (&info, state->search_pspace,
+ symbol_names, FUNCTIONS_DOMAIN);
else
- {
- /* Not all digits -- may be user variable/function or a
- convenience variable. */
+ add_matching_symbols_to_info (name, name_match_type, FUNCTIONS_DOMAIN,
+ &info, state->search_pspace);
+}
- volatile struct gdb_exception exc;
+/* Find all symbols named NAME in FILE_SYMTABS, returning debug symbols
+ in SYMBOLS and minimal symbols in MINSYMS. */
+
+static void
+find_linespec_symbols (struct linespec_state *state,
+ std::vector<symtab *> *file_symtabs,
+ const char *lookup_name,
+ symbol_name_match_type name_match_type,
+ std::vector <block_symbol> *symbols,
+ std::vector<bound_minimal_symbol> *minsyms)
+{
+ std::string canon = cp_canonicalize_string_no_typedefs (lookup_name);
+ if (!canon.empty ())
+ lookup_name = canon.c_str ();
+
+ /* It's important to not call expand_symtabs_matching unnecessarily
+ as it can really slow things down (by unnecessarily expanding
+ potentially 1000s of symtabs, which when debugging some apps can
+ cost 100s of seconds). Avoid this to some extent by *first* calling
+ find_function_symbols, and only if that doesn't find anything
+ *then* call find_method. This handles two important cases:
+ 1) break (anonymous namespace)::foo
+ 2) break class::method where method is in class (and not a baseclass) */
+
+ find_function_symbols (state, file_symtabs, lookup_name,
+ name_match_type, symbols, minsyms);
+
+ /* If we were unable to locate a symbol of the same name, try dividing
+ the name into class and method names and searching the class and its
+ baseclasses. */
+ if (symbols->empty () && minsyms->empty ())
+ {
+ std::string klass, method;
+ const char *last, *p, *scope_op;
- /* Avoid "may be used uninitialized" warning. */
- values.sals = NULL;
- values.nelts = 0;
+ /* 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);
- TRY_CATCH (exc, RETURN_MASK_ERROR)
+ last = NULL;
+ while (p != NULL)
{
- values = decode_variable (self, copy);
+ last = p;
+ p = find_toplevel_string (p + strlen (scope_op), scope_op);
}
- if (exc.reason == 0)
- return values;
+ /* If no scope operator was found, there is nothing more we can do;
+ we already attempted to lookup the entire name as a symbol
+ and failed. */
+ if (last == NULL)
+ return;
- if (exc.error != NOT_FOUND_ERROR)
- throw_exception (exc);
+ /* LOOKUP_NAME points to the class name.
+ LAST points to the method name. */
+ klass = std::string (lookup_name, last - lookup_name);
- /* 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."));
- }
+ /* Skip past the scope operator. */
+ last += strlen (scope_op);
+ method = last;
+
+ /* Find a list of classes named KLASS. */
+ std::vector<block_symbol> classes
+ = lookup_prefix_sym (state, file_symtabs, klass.c_str ());
+ if (!classes.empty ())
+ {
+ /* Now locate a list of suitable methods named METHOD. */
+ try
+ {
+ find_method (state, file_symtabs,
+ klass.c_str (), method.c_str (),
+ &classes, symbols, minsyms);
+ }
- init_sal (&val);
+ /* If successful, we're done. If NOT_FOUND_ERROR
+ was not thrown, rethrow the exception that we did get. */
+ catch (const gdb_exception_error &except)
+ {
+ if (except.error != NOT_FOUND_ERROR)
+ throw;
+ }
+ }
+ }
+}
- values.sals = NULL;
- values.nelts = 0;
+/* Helper for find_label_symbols. Find all labels that match name
+ NAME in BLOCK. Return all labels that match in FUNCTION_SYMBOLS.
+ Return the actual function symbol in which the label was found in
+ LABEL_FUNC_RET. If COMPLETION_MODE is true, then NAME is
+ interpreted as a label name prefix. Otherwise, only a label named
+ exactly NAME match. */
- for (ix = 0; VEC_iterate (symtab_p, self->file_symtabs, ix, elt); ++ix)
+static void
+find_label_symbols_in_block (const struct block *block,
+ const char *name, struct symbol *fn_sym,
+ bool completion_mode,
+ std::vector<block_symbol> *result,
+ std::vector<block_symbol> *label_funcs_ret)
+{
+ if (completion_mode)
{
- if (elt == NULL)
- {
- elt = self->default_symtab;
- set_current_program_space (self->program_space);
- }
- else
- set_current_program_space (SYMTAB_PSPACE (elt));
+ struct block_iterator iter;
+ struct symbol *sym;
+ size_t name_len = strlen (name);
- /* 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;
+ int (*cmp) (const char *, const char *, size_t);
+ cmp = case_sensitivity == case_sensitive_on ? strncmp : strncasecmp;
- add_sal_to_sals (self, &values, &val, NULL);
+ ALL_BLOCK_SYMBOLS (block, iter, sym)
+ {
+ if (symbol_matches_domain (SYMBOL_LANGUAGE (sym),
+ SYMBOL_DOMAIN (sym), LABEL_DOMAIN)
+ && cmp (SYMBOL_SEARCH_NAME (sym), name, name_len) == 0)
+ {
+ result->push_back ({sym, block});
+ label_funcs_ret->push_back ({fn_sym, block});
+ }
+ }
}
-
- if (self->canonical)
+ else
{
- 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);
- }
+ struct block_symbol label_sym
+ = lookup_symbol (name, block, LABEL_DOMAIN, 0);
- return values;
+ if (label_sym.symbol != NULL)
+ {
+ result->push_back (label_sym);
+ label_funcs_ret->push_back ({fn_sym, block});
+ }
+ }
}
-\f
+/* Return all labels that match name NAME in FUNCTION_SYMBOLS or NULL
+ if no matches were found.
-/* 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 the actual function symbol in which the label was found in
+ LABEL_FUNC_RET. If COMPLETION_MODE is true, then NAME is
+ interpreted as a label name prefix. Otherwise, only labels named
+ exactly NAME match. */
-static int
-decode_label (struct linespec_state *self,
- VEC (symbolp) *function_symbols, char *copy,
- struct symtabs_and_lines *result)
+
+static std::vector<block_symbol> *
+find_label_symbols (struct linespec_state *self,
+ std::vector<block_symbol> *function_symbols,
+ std::vector<block_symbol> *label_funcs_ret,
+ const char *name,
+ bool completion_mode)
{
+ const struct block *block;
struct symbol *fn_sym;
- int ix;
+ std::vector<block_symbol> result;
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);
+ block = get_current_search_block ();
for (;
block && !BLOCK_FUNCTION (block);
block = BLOCK_SUPERBLOCK (block))
;
if (!block)
- return 0;
+ return NULL;
fn_sym = BLOCK_FUNCTION (block);
- sym = lookup_symbol (copy, block, LABEL_DOMAIN, 0);
+ find_label_symbols_in_block (block, name, fn_sym, completion_mode,
+ &result, label_funcs_ret);
+ }
+ else
+ {
+ for (const auto &elt : *function_symbols)
+ {
+ fn_sym = elt.symbol;
+ set_current_program_space (SYMTAB_PSPACE (symbol_symtab (fn_sym)));
+ block = SYMBOL_BLOCK_VALUE (fn_sym);
- if (sym == NULL)
- return 0;
+ find_label_symbols_in_block (block, name, fn_sym, completion_mode,
+ &result, label_funcs_ret);
+ }
+ }
- symbol_to_sal (&sal, self->funfirstline, sym);
- add_sal_to_sals (self, &values, &sal,
- SYMBOL_NATURAL_NAME (fn_sym));
+ if (!result.empty ())
+ return new std::vector<block_symbol> (std::move (result));
+ return nullptr;
+}
- if (self->canonical)
- {
- self->canonical->special_display = 1;
- self->canonical->addr_string
- = xstrprintf ("%s:%s", SYMBOL_NATURAL_NAME (fn_sym),
- copy);
- }
+\f
- *result = values;
+/* A helper for create_sals_line_offset that handles the 'list_mode' case. */
- return 1;
- }
+static std::vector<symtab_and_line>
+decode_digits_list_mode (struct linespec_state *self,
+ linespec_p ls,
+ struct symtab_and_line val)
+{
+ gdb_assert (self->list_mode);
- result->sals = NULL;
- result->nelts = 0;
+ std::vector<symtab_and_line> values;
- for (ix = 0; VEC_iterate (symbolp, function_symbols, ix, fn_sym); ++ix)
+ for (const auto &elt : *ls->file_symtabs)
{
- struct block *block;
- struct symbol *sym;
+ /* 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)
- {
- struct symtab_and_line sal;
- char *symname;
+ /* 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 = true;
- 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);
- }
+ add_sal_to_sals (self, &values, &val, NULL, 0);
}
- if (self->canonical && result->nelts > 0)
+ return values;
+}
+
+/* A helper for create_sals_line_offset that iterates over the symtabs,
+ adding lines to the VEC. */
+
+static std::vector<symtab_and_line>
+decode_digits_ordinary (struct linespec_state *self,
+ linespec_p ls,
+ int line,
+ struct linetable_entry **best_entry)
+{
+ std::vector<symtab_and_line> sals;
+ for (const auto &elt : *ls->file_symtabs)
{
- self->canonical->pre_expanded = 1;
- self->canonical->special_display = 1;
+ std::vector<CORE_ADDR> pcs;
+
+ /* The logic above should ensure this. */
+ gdb_assert (elt != NULL);
- gdb_assert (self->user_function);
- self->canonical->addr_string
- = xstrprintf ("%s:%s", self->user_function, copy);
+ set_current_program_space (SYMTAB_PSPACE (elt));
+
+ pcs = find_pcs_for_symtab_line (elt, line, best_entry);
+ for (CORE_ADDR pc : pcs)
+ {
+ symtab_and_line sal;
+ sal.pspace = SYMTAB_PSPACE (elt);
+ sal.symtab = elt;
+ sal.line = line;
+ sal.explicit_line = true;
+ sal.pc = pc;
+ sals.push_back (std::move (sal));
+ }
}
- return result->nelts > 0;
+ return sals;
}
-/* A callback used to possibly add a symbol to the results. */
+\f
+
+/* Return the line offset represented by VARIABLE. */
-static int
-collect_symbols (struct symbol *sym, void *data)
+static struct line_offset
+linespec_parse_variable (struct linespec_state *self, const char *variable)
{
- struct collect_info *info = data;
- struct symtab_and_line sal;
+ 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. */
+ {
+ /* We have a value history reference. */
+ struct value *val_history;
- 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));
+ 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 1; /* Continue iterating. */
+ return offset;
}
+\f
-/* 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. This function should return SALs
+ matching those from find_function_start_sal, otherwise false
+ multiple-locations breakpoints could be placed. */
static void
minsym_found (struct linespec_state *self, struct objfile *objfile,
struct minimal_symbol *msymbol,
- struct symtabs_and_lines *result)
+ std::vector<symtab_and_line> *result)
{
- struct gdbarch *gdbarch = get_objfile_arch (objfile);
- CORE_ADDR pc;
- struct symtab_and_line sal;
-
- sal = find_pc_sect_line (SYMBOL_VALUE_ADDRESS (msymbol),
- (struct obj_section *) 0, 0);
- sal.section = SYMBOL_OBJ_SECTION (msymbol);
-
- /* The minimal symbol might point to a function descriptor;
- resolve it to the actual code address instead. */
- pc = gdbarch_convert_from_func_ptr_addr (gdbarch, sal.pc, ¤t_target);
- if (pc != sal.pc)
- sal = find_pc_sect_line (pc, NULL, 0);
-
- if (self->funfirstline)
- skip_prologue_sal (&sal);
-
- if (maybe_add_address (self->addr_set, objfile->pspace, sal.pc))
- 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. */
+ bool want_start_sal;
-typedef struct minsym_and_objfile
-{
- struct minimal_symbol *minsym;
- struct objfile *objfile;
-} minsym_and_objfile_d;
+ CORE_ADDR func_addr;
+ bool is_function = msymbol_is_function (objfile, msymbol, &func_addr);
-DEF_VEC_O (minsym_and_objfile_d);
+ if (is_function)
+ {
+ const char *msym_name = MSYMBOL_LINKAGE_NAME (msymbol);
-/* A helper struct to pass some data through
- iterate_over_minimal_symbols. */
+ if (MSYMBOL_TYPE (msymbol) == mst_text_gnu_ifunc
+ || MSYMBOL_TYPE (msymbol) == mst_data_gnu_ifunc)
+ want_start_sal = gnu_ifunc_resolve_name (msym_name, &func_addr);
+ else
+ want_start_sal = true;
+ }
-struct collect_minsyms
-{
- /* The objfile we're examining. */
- struct objfile *objfile;
+ symtab_and_line sal;
- /* The funfirstline setting from the initial call. */
- int funfirstline;
+ if (is_function && want_start_sal)
+ sal = find_function_start_sal (func_addr, NULL, self->funfirstline);
+ else
+ {
+ sal.objfile = objfile;
+ sal.msymbol = msymbol;
+ /* Store func_addr, not the minsym's address in case this was an
+ ifunc that hasn't been resolved yet. */
+ if (is_function)
+ sal.pc = func_addr;
+ else
+ sal.pc = MSYMBOL_VALUE_ADDRESS (objfile, msymbol);
+ sal.pspace = current_program_space;
+ }
- /* The list_mode setting from the initial call. */
- int list_mode;
+ sal.section = MSYMBOL_OBJ_SECTION (objfile, msymbol);
- /* The resulting symbols. */
- VEC (minsym_and_objfile_d) *msyms;
-};
+ if (maybe_add_address (self->addr_set, objfile->pspace, sal.pc))
+ add_sal_to_sals (self, result, &sal, MSYMBOL_NATURAL_NAME (msymbol), 0);
+}
/* A helper function to classify a minimal_symbol_type according to
priority. */
}
}
-/* Callback for qsort that sorts symbols by priority. */
+/* Callback for std::sort that sorts symbols by priority. */
-static int
-compare_msyms (const void *a, const void *b)
+static bool
+compare_msyms (const bound_minimal_symbol &a, const bound_minimal_symbol &b)
{
- const minsym_and_objfile_d *moa = a;
- const minsym_and_objfile_d *mob = b;
- enum minimal_symbol_type ta = MSYMBOL_TYPE (moa->minsym);
- enum minimal_symbol_type tb = MSYMBOL_TYPE (mob->minsym);
+ enum minimal_symbol_type ta = MSYMBOL_TYPE (a.minsym);
+ enum minimal_symbol_type tb = MSYMBOL_TYPE (b.minsym);
- return classify_mtype (ta) - classify_mtype (tb);
+ return classify_mtype (ta) < classify_mtype (tb);
}
-/* Callback for iterate_over_minimal_symbols that adds the symbol to
- the result. */
+/* Helper for search_minsyms_for_name that adds the symbol to the
+ result. */
static void
-add_minsym (struct minimal_symbol *minsym, void *d)
+add_minsym (struct minimal_symbol *minsym, struct objfile *objfile,
+ struct symtab *symtab, int list_mode,
+ std::vector<struct bound_minimal_symbol> *msyms)
{
- struct collect_minsyms *info = d;
- minsym_and_objfile_d mo;
+ if (symtab != NULL)
+ {
+ /* We're looking for a label for which we don't have debug
+ info. */
+ CORE_ADDR func_addr;
+ if (msymbol_is_function (objfile, minsym, &func_addr))
+ {
+ symtab_and_line sal = find_pc_sect_line (func_addr, NULL, 0);
- /* Exclude data symbols when looking for breakpoint locations. */
- if (!info->list_mode)
- switch (minsym->type)
- {
- case mst_slot_got_plt:
- case mst_data:
- case mst_bss:
- case mst_abs:
- case mst_file_data:
- case mst_file_bss:
- {
- /* Make sure this minsym is not a function descriptor
- before we decide to discard it. */
- struct gdbarch *gdbarch = info->objfile->gdbarch;
- CORE_ADDR addr = gdbarch_convert_from_func_ptr_addr
- (gdbarch, SYMBOL_VALUE_ADDRESS (minsym),
- ¤t_target);
-
- if (addr == SYMBOL_VALUE_ADDRESS (minsym))
- return;
- }
- }
+ if (symtab != sal.symtab)
+ return;
+ }
+ }
- mo.minsym = minsym;
- mo.objfile = info->objfile;
- VEC_safe_push (minsym_and_objfile_d, info->msyms, &mo);
+ /* Exclude data symbols when looking for breakpoint locations. */
+ if (!list_mode && !msymbol_is_function (objfile, minsym))
+ return;
+
+ struct bound_minimal_symbol mo = {minsym, objfile};
+ msyms->push_back (mo);
+ return;
}
-/* Search minimal symbols in all objfiles for NAME. If SEARCH_PSPACE
+/* Search for minimal symbols called NAME. If SEARCH_PSPACE
is not NULL, the search is restricted to just that program
- space. */
+ space.
+
+ If SYMTAB is NULL, search all objfiles, otherwise
+ restrict results to the given SYMTAB. */
static void
-search_minsyms_for_name (struct collect_info *info, const char *name,
- struct program_space *search_pspace)
+search_minsyms_for_name (struct collect_info *info,
+ const lookup_name_info &name,
+ struct program_space *search_pspace,
+ struct symtab *symtab)
{
- struct objfile *objfile;
- struct program_space *pspace;
-
- ALL_PSPACES (pspace)
- {
- struct collect_minsyms local;
- struct cleanup *cleanup;
-
- if (search_pspace != NULL && search_pspace != pspace)
- continue;
- if (pspace->executing_startup)
- continue;
+ std::vector<struct bound_minimal_symbol> minsyms;
- set_current_program_space (pspace);
+ if (symtab == NULL)
+ {
+ struct program_space *pspace;
- memset (&local, 0, sizeof (local));
- local.funfirstline = info->state->funfirstline;
- local.list_mode = info->state->list_mode;
+ ALL_PSPACES (pspace)
+ {
+ if (search_pspace != NULL && search_pspace != pspace)
+ continue;
+ if (pspace->executing_startup)
+ continue;
- cleanup = make_cleanup (VEC_cleanup (minsym_and_objfile_d),
- &local.msyms);
+ set_current_program_space (pspace);
- ALL_OBJFILES (objfile)
+ for (objfile *objfile : current_program_space->objfiles ())
+ {
+ iterate_over_minimal_symbols (objfile, name,
+ [&] (struct minimal_symbol *msym)
+ {
+ add_minsym (msym, objfile, nullptr,
+ info->state->list_mode,
+ &minsyms);
+ return false;
+ });
+ }
+ }
+ }
+ else
{
- local.objfile = objfile;
- iterate_over_minimal_symbols (objfile, name, add_minsym, &local);
+ if (search_pspace == NULL || SYMTAB_PSPACE (symtab) == search_pspace)
+ {
+ set_current_program_space (SYMTAB_PSPACE (symtab));
+ iterate_over_minimal_symbols
+ (SYMTAB_OBJFILE (symtab), name,
+ [&] (struct minimal_symbol *msym)
+ {
+ add_minsym (msym, SYMTAB_OBJFILE (symtab), symtab,
+ info->state->list_mode, &minsyms);
+ return false;
+ });
+ }
}
- if (!VEC_empty (minsym_and_objfile_d, local.msyms))
- {
- int classification;
- int ix;
- minsym_and_objfile_d *item;
-
- qsort (VEC_address (minsym_and_objfile_d, local.msyms),
- VEC_length (minsym_and_objfile_d, local.msyms),
- sizeof (minsym_and_objfile_d),
- compare_msyms);
-
- /* Now the minsyms are in classification order. So, we walk
- over them and process just the minsyms with the same
- classification as the very first minsym in the list. */
- item = VEC_index (minsym_and_objfile_d, local.msyms, 0);
- classification = classify_mtype (MSYMBOL_TYPE (item->minsym));
-
- for (ix = 0;
- VEC_iterate (minsym_and_objfile_d, local.msyms, ix, item);
- ++ix)
- {
- if (classify_mtype (MSYMBOL_TYPE (item->minsym)) != classification)
- break;
+ if (!minsyms.empty ())
+ {
+ int classification;
- minsym_found (info->state, item->objfile, item->minsym,
- &info->result);
- }
- }
+ std::sort (minsyms.begin (), minsyms.end (), compare_msyms);
- do_cleanups (cleanup);
- }
+ /* Now the minsyms are in classification order. So, we walk
+ over them and process just the minsyms with the same
+ classification as the very first minsym in the list. */
+ classification = classify_mtype (MSYMBOL_TYPE (minsyms[0].minsym));
+
+ for (const bound_minimal_symbol &item : minsyms)
+ {
+ if (classify_mtype (MSYMBOL_TYPE (item.minsym)) != classification)
+ break;
+
+ info->result.minimal_symbols->push_back (item);
+ }
+ }
}
/* A helper function to add all symbols matching NAME to INFO. If
static void
add_matching_symbols_to_info (const char *name,
+ symbol_name_match_type name_match_type,
+ enum search_domain search_domain,
struct collect_info *info,
struct program_space *pspace)
{
- int ix;
- struct symtab *elt;
+ lookup_name_info lookup_name (name, name_match_type);
- for (ix = 0; VEC_iterate (symtab_p, info->state->file_symtabs, ix, elt); ++ix)
+ for (const auto &elt : *info->file_symtabs)
{
- struct symbol *sym;
-
- if (elt == NULL)
+ if (elt == nullptr)
{
- iterate_over_all_matching_symtabs (name, VAR_DOMAIN,
- collect_symbols, info,
- pspace);
- search_minsyms_for_name (info, name, pspace);
+ iterate_over_all_matching_symtabs (info->state, lookup_name,
+ VAR_DOMAIN, search_domain,
+ pspace, true,
+ [&] (block_symbol *bsym)
+ { return info->add_symbol (bsym); });
+ search_minsyms_for_name (info, lookup_name, pspace, NULL);
}
else if (pspace == NULL || pspace == SYMTAB_PSPACE (elt))
{
+ int prev_len = info->result.symbols->size ();
+
/* Program spaces that are executing startup should have
been filtered out earlier. */
gdb_assert (!SYMTAB_PSPACE (elt)->executing_startup);
set_current_program_space (SYMTAB_PSPACE (elt));
- LA_ITERATE_OVER_SYMBOLS (get_search_block (elt), name,
- VAR_DOMAIN, collect_symbols,
- info);
- }
- }
-}
-
-/* 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);
+ iterate_over_file_blocks (elt, lookup_name, VAR_DOMAIN,
+ [&] (block_symbol *bsym)
+ { return info->add_symbol (bsym); });
+
+ /* If no new symbols were found in this iteration and this symtab
+ is in assembler, we might actually be looking for a label for
+ which we don't have debug info. Check for a minimal symbol in
+ this case. */
+ if (prev_len == info->result.symbols->size ()
+ && elt->language == language_asm)
+ search_minsyms_for_name (info, lookup_name, pspace, elt);
}
- 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);
}
-
\f
/* Now come some functions that are called from multiple places within
{
if (SYMBOL_CLASS (sym) == LOC_LABEL && SYMBOL_VALUE_ADDRESS (sym) != 0)
{
- init_sal (result);
- result->symtab = SYMBOL_SYMTAB (sym);
+ *result = {};
+ result->symtab = symbol_symtab (sym);
+ result->symbol = sym;
result->line = SYMBOL_LINE (sym);
result->pc = SYMBOL_VALUE_ADDRESS (sym);
- result->pspace = SYMTAB_PSPACE (SYMBOL_SYMTAB (sym));
+ result->pspace = SYMTAB_PSPACE (result->symtab);
result->explicit_pc = 1;
return 1;
}
else if (SYMBOL_LINE (sym) != 0)
{
/* We know its line number. */
- init_sal (result);
- result->symtab = SYMBOL_SYMTAB (sym);
+ *result = {};
+ result->symtab = symbol_symtab (sym);
+ result->symbol = sym;
result->line = SYMBOL_LINE (sym);
- result->pspace = SYMTAB_PSPACE (SYMBOL_SYMTAB (sym));
+ result->pc = SYMBOL_VALUE_ADDRESS (sym);
+ result->pspace = SYMTAB_PSPACE (result->symtab);
return 1;
}
}
return 0;
}
-/* See the comment in linespec.h. */
-
-void
-init_linespec_result (struct linespec_result *lr)
-{
- memset (lr, 0, sizeof (*lr));
-}
-
-/* See the comment in linespec.h. */
-
-void
-destroy_linespec_result (struct linespec_result *ls)
-{
- int i;
- struct linespec_sals *lsal;
-
- xfree (ls->addr_string);
- for (i = 0; VEC_iterate (linespec_sals, ls->sals, i, lsal); ++i)
- {
- xfree (lsal->canonical);
- xfree (lsal->sals.sals);
- }
- VEC_free (linespec_sals, ls->sals);
-}
-
-/* Cleanup function for a linespec_result. */
-
-static void
-cleanup_linespec_result (void *a)
+linespec_result::~linespec_result ()
{
- destroy_linespec_result (a);
+ for (linespec_sals &lsal : lsals)
+ xfree (lsal.canonical);
}
-/* See the comment in linespec.h. */
+/* Return the quote characters permitted by the linespec parser. */
-struct cleanup *
-make_cleanup_destroy_linespec_result (struct linespec_result *ls)
+const char *
+get_gdb_linespec_parser_quote_characters (void)
{
- return make_cleanup (cleanup_linespec_result, ls);
+ return linespec_quote_characters;
}