| /* Parse expressions for GDB. |
| |
| Copyright (C) 1986-2023 Free Software Foundation, Inc. |
| |
| Modified from expread.y by the Department of Computer Science at the |
| State University of New York at Buffalo, 1991. |
| |
| This file is part of GDB. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
| |
| /* Parse an expression from text in a string, |
| and return the result as a struct expression pointer. |
| That structure contains arithmetic operations in reverse polish, |
| with constants represented by operations that are followed by special data. |
| See expression.h for the details of the format. |
| What is important here is that it can be built up sequentially |
| during the process of parsing; the lower levels of the tree always |
| come first in the result. */ |
| |
| #include "defs.h" |
| #include <ctype.h> |
| #include "arch-utils.h" |
| #include "symtab.h" |
| #include "gdbtypes.h" |
| #include "frame.h" |
| #include "expression.h" |
| #include "value.h" |
| #include "command.h" |
| #include "language.h" |
| #include "parser-defs.h" |
| #include "gdbcmd.h" |
| #include "symfile.h" |
| #include "inferior.h" |
| #include "target-float.h" |
| #include "block.h" |
| #include "source.h" |
| #include "objfiles.h" |
| #include "user-regs.h" |
| #include <algorithm> |
| #include <optional> |
| #include "c-exp.h" |
| |
| static unsigned int expressiondebug = 0; |
| static void |
| show_expressiondebug (struct ui_file *file, int from_tty, |
| struct cmd_list_element *c, const char *value) |
| { |
| gdb_printf (file, _("Expression debugging is %s.\n"), value); |
| } |
| |
| |
| /* True if an expression parser should set yydebug. */ |
| static bool parser_debug; |
| |
| static void |
| show_parserdebug (struct ui_file *file, int from_tty, |
| struct cmd_list_element *c, const char *value) |
| { |
| gdb_printf (file, _("Parser debugging is %s.\n"), value); |
| } |
| |
| |
| /* Documented at it's declaration. */ |
| |
| void |
| innermost_block_tracker::update (const struct block *b, |
| innermost_block_tracker_types t) |
| { |
| if ((m_types & t) != 0 |
| && (m_innermost_block == NULL |
| || m_innermost_block->contains (b))) |
| m_innermost_block = b; |
| } |
| |
| |
| |
| bool |
| expr_complete_tag::complete (struct expression *exp, |
| completion_tracker &tracker) |
| { |
| collect_symbol_completion_matches_type (tracker, m_name.get (), |
| m_name.get (), m_code); |
| return true; |
| } |
| |
| /* See parser-defs.h. */ |
| |
| void |
| parser_state::mark_struct_expression (expr::structop_base_operation *op) |
| { |
| gdb_assert (parse_completion && m_completion_state == nullptr); |
| m_completion_state.reset (new expr_complete_structop (op)); |
| } |
| |
| /* Indicate that the current parser invocation is completing a tag. |
| TAG is the type code of the tag, and PTR and LENGTH represent the |
| start of the tag name. */ |
| |
| void |
| parser_state::mark_completion_tag (enum type_code tag, const char *ptr, |
| int length) |
| { |
| gdb_assert (parse_completion && m_completion_state == nullptr); |
| gdb_assert (tag == TYPE_CODE_UNION |
| || tag == TYPE_CODE_STRUCT |
| || tag == TYPE_CODE_ENUM); |
| m_completion_state.reset |
| (new expr_complete_tag (tag, make_unique_xstrndup (ptr, length))); |
| } |
| |
| /* See parser-defs.h. */ |
| |
| void |
| parser_state::push_c_string (int kind, struct stoken_vector *vec) |
| { |
| std::vector<std::string> data (vec->len); |
| for (int i = 0; i < vec->len; ++i) |
| data[i] = std::string (vec->tokens[i].ptr, vec->tokens[i].length); |
| |
| push_new<expr::c_string_operation> ((enum c_string_type_values) kind, |
| std::move (data)); |
| } |
| |
| /* See parser-defs.h. */ |
| |
| void |
| parser_state::push_symbol (const char *name, block_symbol sym) |
| { |
| if (sym.symbol != nullptr) |
| { |
| if (symbol_read_needs_frame (sym.symbol)) |
| block_tracker->update (sym); |
| push_new<expr::var_value_operation> (sym); |
| } |
| else |
| { |
| struct bound_minimal_symbol msymbol = lookup_bound_minimal_symbol (name); |
| if (msymbol.minsym != NULL) |
| push_new<expr::var_msym_value_operation> (msymbol); |
| else if (!have_full_symbols () && !have_partial_symbols ()) |
| error (_("No symbol table is loaded. Use the \"file\" command.")); |
| else |
| error (_("No symbol \"%s\" in current context."), name); |
| } |
| } |
| |
| /* See parser-defs.h. */ |
| |
| void |
| parser_state::push_dollar (struct stoken str) |
| { |
| struct block_symbol sym; |
| struct bound_minimal_symbol msym; |
| struct internalvar *isym = NULL; |
| std::string copy; |
| |
| /* Handle the tokens $digits; also $ (short for $0) and $$ (short for $$1) |
| and $$digits (equivalent to $<-digits> if you could type that). */ |
| |
| int negate = 0; |
| int i = 1; |
| /* Double dollar means negate the number and add -1 as well. |
| Thus $$ alone means -1. */ |
| if (str.length >= 2 && str.ptr[1] == '$') |
| { |
| negate = 1; |
| i = 2; |
| } |
| if (i == str.length) |
| { |
| /* Just dollars (one or two). */ |
| i = -negate; |
| push_new<expr::last_operation> (i); |
| return; |
| } |
| /* Is the rest of the token digits? */ |
| for (; i < str.length; i++) |
| if (!(str.ptr[i] >= '0' && str.ptr[i] <= '9')) |
| break; |
| if (i == str.length) |
| { |
| i = atoi (str.ptr + 1 + negate); |
| if (negate) |
| i = -i; |
| push_new<expr::last_operation> (i); |
| return; |
| } |
| |
| /* Handle tokens that refer to machine registers: |
| $ followed by a register name. */ |
| i = user_reg_map_name_to_regnum (gdbarch (), |
| str.ptr + 1, str.length - 1); |
| if (i >= 0) |
| { |
| str.length--; |
| str.ptr++; |
| push_new<expr::register_operation> (copy_name (str)); |
| block_tracker->update (expression_context_block, |
| INNERMOST_BLOCK_FOR_REGISTERS); |
| return; |
| } |
| |
| /* Any names starting with $ are probably debugger internal variables. */ |
| |
| copy = copy_name (str); |
| isym = lookup_only_internalvar (copy.c_str () + 1); |
| if (isym) |
| { |
| push_new<expr::internalvar_operation> (isym); |
| return; |
| } |
| |
| /* On some systems, such as HP-UX and hppa-linux, certain system routines |
| have names beginning with $ or $$. Check for those, first. */ |
| |
| sym = lookup_symbol (copy.c_str (), NULL, VAR_DOMAIN, NULL); |
| if (sym.symbol) |
| { |
| push_new<expr::var_value_operation> (sym); |
| return; |
| } |
| msym = lookup_bound_minimal_symbol (copy.c_str ()); |
| if (msym.minsym) |
| { |
| push_new<expr::var_msym_value_operation> (msym); |
| return; |
| } |
| |
| /* Any other names are assumed to be debugger internal variables. */ |
| |
| push_new<expr::internalvar_operation> |
| (create_internalvar (copy.c_str () + 1)); |
| } |
| |
| |
| |
| const char * |
| find_template_name_end (const char *p) |
| { |
| int depth = 1; |
| int just_seen_right = 0; |
| int just_seen_colon = 0; |
| int just_seen_space = 0; |
| |
| if (!p || (*p != '<')) |
| return 0; |
| |
| while (*++p) |
| { |
| switch (*p) |
| { |
| case '\'': |
| case '\"': |
| case '{': |
| case '}': |
| /* In future, may want to allow these?? */ |
| return 0; |
| case '<': |
| depth++; /* start nested template */ |
| if (just_seen_colon || just_seen_right || just_seen_space) |
| return 0; /* but not after : or :: or > or space */ |
| break; |
| case '>': |
| if (just_seen_colon || just_seen_right) |
| return 0; /* end a (nested?) template */ |
| just_seen_right = 1; /* but not after : or :: */ |
| if (--depth == 0) /* also disallow >>, insist on > > */ |
| return ++p; /* if outermost ended, return */ |
| break; |
| case ':': |
| if (just_seen_space || (just_seen_colon > 1)) |
| return 0; /* nested class spec coming up */ |
| just_seen_colon++; /* we allow :: but not :::: */ |
| break; |
| case ' ': |
| break; |
| default: |
| if (!((*p >= 'a' && *p <= 'z') || /* allow token chars */ |
| (*p >= 'A' && *p <= 'Z') || |
| (*p >= '0' && *p <= '9') || |
| (*p == '_') || (*p == ',') || /* commas for template args */ |
| (*p == '&') || (*p == '*') || /* pointer and ref types */ |
| (*p == '(') || (*p == ')') || /* function types */ |
| (*p == '[') || (*p == ']'))) /* array types */ |
| return 0; |
| } |
| if (*p != ' ') |
| just_seen_space = 0; |
| if (*p != ':') |
| just_seen_colon = 0; |
| if (*p != '>') |
| just_seen_right = 0; |
| } |
| return 0; |
| } |
| |
| |
| /* Return a null-terminated temporary copy of the name of a string token. |
| |
| Tokens that refer to names do so with explicit pointer and length, |
| so they can share the storage that lexptr is parsing. |
| When it is necessary to pass a name to a function that expects |
| a null-terminated string, the substring is copied out |
| into a separate block of storage. */ |
| |
| std::string |
| copy_name (struct stoken token) |
| { |
| return std::string (token.ptr, token.length); |
| } |
| |
| |
| /* As for parse_exp_1, except that if VOID_CONTEXT_P, then |
| no value is expected from the expression. */ |
| |
| static expression_up |
| parse_exp_in_context (const char **stringptr, CORE_ADDR pc, |
| const struct block *block, |
| parser_flags flags, |
| innermost_block_tracker *tracker, |
| std::unique_ptr<expr_completion_base> *completer) |
| { |
| const struct language_defn *lang = NULL; |
| |
| if (*stringptr == 0 || **stringptr == 0) |
| error_no_arg (_("expression to compute")); |
| |
| const struct block *expression_context_block = block; |
| CORE_ADDR expression_context_pc = 0; |
| |
| innermost_block_tracker local_tracker; |
| if (tracker == nullptr) |
| tracker = &local_tracker; |
| |
| if ((flags & PARSER_LEAVE_BLOCK_ALONE) == 0) |
| { |
| /* If no context specified, try using the current frame, if any. */ |
| if (!expression_context_block) |
| expression_context_block |
| = get_selected_block (&expression_context_pc); |
| else if (pc == 0) |
| expression_context_pc = expression_context_block->entry_pc (); |
| else |
| expression_context_pc = pc; |
| |
| /* Fall back to using the current source static context, if any. */ |
| |
| if (!expression_context_block) |
| { |
| struct symtab_and_line cursal |
| = get_current_source_symtab_and_line (); |
| |
| if (cursal.symtab) |
| expression_context_block |
| = cursal.symtab->compunit ()->blockvector ()->static_block (); |
| |
| if (expression_context_block) |
| expression_context_pc = expression_context_block->entry_pc (); |
| } |
| } |
| |
| if (language_mode == language_mode_auto && block != NULL) |
| { |
| /* Find the language associated to the given context block. |
| Default to the current language if it can not be determined. |
| |
| Note that using the language corresponding to the current frame |
| can sometimes give unexpected results. For instance, this |
| routine is often called several times during the inferior |
| startup phase to re-parse breakpoint expressions after |
| a new shared library has been loaded. The language associated |
| to the current frame at this moment is not relevant for |
| the breakpoint. Using it would therefore be silly, so it seems |
| better to rely on the current language rather than relying on |
| the current frame language to parse the expression. That's why |
| we do the following language detection only if the context block |
| has been specifically provided. */ |
| struct symbol *func = block->linkage_function (); |
| |
| if (func != NULL) |
| lang = language_def (func->language ()); |
| if (lang == NULL || lang->la_language == language_unknown) |
| lang = current_language; |
| } |
| else |
| lang = current_language; |
| |
| /* get_current_arch may reset CURRENT_LANGUAGE via select_frame. |
| While we need CURRENT_LANGUAGE to be set to LANG (for lookup_symbol |
| and others called from *.y) ensure CURRENT_LANGUAGE gets restored |
| to the value matching SELECTED_FRAME as set by get_current_arch. */ |
| |
| parser_state ps (lang, get_current_arch (), expression_context_block, |
| expression_context_pc, flags, *stringptr, |
| completer != nullptr, tracker); |
| |
| scoped_restore_current_language lang_saver; |
| set_language (lang->la_language); |
| |
| try |
| { |
| lang->parser (&ps); |
| } |
| catch (const gdb_exception_error &except) |
| { |
| /* If parsing for completion, allow this to succeed; but if no |
| expression elements have been written, then there's nothing |
| to do, so fail. */ |
| if (! ps.parse_completion || ps.expout->op == nullptr) |
| throw; |
| } |
| |
| expression_up result = ps.release (); |
| result->op->set_outermost (); |
| |
| if (expressiondebug) |
| result->dump (gdb_stdlog); |
| |
| if (completer != nullptr) |
| *completer = std::move (ps.m_completion_state); |
| *stringptr = ps.lexptr; |
| return result; |
| } |
| |
| /* Read an expression from the string *STRINGPTR points to, |
| parse it, and return a pointer to a struct expression that we malloc. |
| Use block BLOCK as the lexical context for variable names; |
| if BLOCK is zero, use the block of the selected stack frame. |
| Meanwhile, advance *STRINGPTR to point after the expression, |
| at the first nonwhite character that is not part of the expression |
| (possibly a null character). FLAGS are passed to the parser. */ |
| |
| expression_up |
| parse_exp_1 (const char **stringptr, CORE_ADDR pc, const struct block *block, |
| parser_flags flags, innermost_block_tracker *tracker) |
| { |
| return parse_exp_in_context (stringptr, pc, block, flags, |
| tracker, nullptr); |
| } |
| |
| /* Parse STRING as an expression, and complain if this fails to use up |
| all of the contents of STRING. TRACKER, if non-null, will be |
| updated by the parser. FLAGS are passed to the parser. */ |
| |
| expression_up |
| parse_expression (const char *string, innermost_block_tracker *tracker, |
| parser_flags flags) |
| { |
| expression_up exp = parse_exp_in_context (&string, 0, nullptr, flags, |
| tracker, nullptr); |
| if (*string) |
| error (_("Junk after end of expression.")); |
| return exp; |
| } |
| |
| /* Same as parse_expression, but using the given language (LANG) |
| to parse the expression. */ |
| |
| expression_up |
| parse_expression_with_language (const char *string, enum language lang) |
| { |
| std::optional<scoped_restore_current_language> lang_saver; |
| if (current_language->la_language != lang) |
| { |
| lang_saver.emplace (); |
| set_language (lang); |
| } |
| |
| return parse_expression (string); |
| } |
| |
| /* Parse STRING as an expression. If the parse is marked for |
| completion, set COMPLETER and return the expression. In all other |
| cases, return NULL. */ |
| |
| expression_up |
| parse_expression_for_completion |
| (const char *string, |
| std::unique_ptr<expr_completion_base> *completer) |
| { |
| expression_up exp; |
| |
| try |
| { |
| exp = parse_exp_in_context (&string, 0, 0, 0, nullptr, completer); |
| } |
| catch (const gdb_exception_error &except) |
| { |
| /* Nothing, EXP remains NULL. */ |
| } |
| |
| /* If we didn't get a completion result, be sure to also not return |
| an expression to our caller. */ |
| if (*completer == nullptr) |
| return nullptr; |
| |
| return exp; |
| } |
| |
| /* Parse floating point value P of length LEN. |
| Return false if invalid, true if valid. |
| The successfully parsed number is stored in DATA in |
| target format for floating-point type TYPE. |
| |
| NOTE: This accepts the floating point syntax that sscanf accepts. */ |
| |
| bool |
| parse_float (const char *p, int len, |
| const struct type *type, gdb_byte *data) |
| { |
| return target_float_from_string (data, type, std::string (p, len)); |
| } |
| |
| /* Return true if the number N_SIGN * N fits in a type with TYPE_BITS and |
| TYPE_SIGNED_P. N_SIGNED is either 1 or -1. */ |
| |
| bool |
| fits_in_type (int n_sign, ULONGEST n, int type_bits, bool type_signed_p) |
| { |
| /* Normalize -0. */ |
| if (n == 0 && n_sign == -1) |
| n_sign = 1; |
| |
| if (n_sign == -1 && !type_signed_p) |
| /* Can't fit a negative number in an unsigned type. */ |
| return false; |
| |
| if (type_bits > sizeof (ULONGEST) * 8) |
| return true; |
| |
| ULONGEST smax = (ULONGEST)1 << (type_bits - 1); |
| if (n_sign == -1) |
| { |
| /* Negative number, signed type. */ |
| return (n <= smax); |
| } |
| else if (n_sign == 1 && type_signed_p) |
| { |
| /* Positive number, signed type. */ |
| return (n < smax); |
| } |
| else if (n_sign == 1 && !type_signed_p) |
| { |
| /* Positive number, unsigned type. */ |
| return ((n >> 1) >> (type_bits - 1)) == 0; |
| } |
| else |
| gdb_assert_not_reached (""); |
| } |
| |
| /* Return true if the number N_SIGN * N fits in a type with TYPE_BITS and |
| TYPE_SIGNED_P. N_SIGNED is either 1 or -1. */ |
| |
| bool |
| fits_in_type (int n_sign, const gdb_mpz &n, int type_bits, bool type_signed_p) |
| { |
| /* N must be nonnegative. */ |
| gdb_assert (n.sgn () >= 0); |
| |
| /* Zero always fits. */ |
| /* Normalize -0. */ |
| if (n.sgn () == 0) |
| return true; |
| |
| if (n_sign == -1 && !type_signed_p) |
| /* Can't fit a negative number in an unsigned type. */ |
| return false; |
| |
| gdb_mpz max = gdb_mpz::pow (2, (type_signed_p |
| ? type_bits - 1 |
| : type_bits)); |
| if (n_sign == -1) |
| return n <= max; |
| return n < max; |
| } |
| |
| /* This function avoids direct calls to fprintf |
| in the parser generated debug code. */ |
| void |
| parser_fprintf (FILE *x, const char *y, ...) |
| { |
| va_list args; |
| |
| va_start (args, y); |
| if (x == stderr) |
| gdb_vprintf (gdb_stderr, y, args); |
| else |
| { |
| gdb_printf (gdb_stderr, " Unknown FILE used.\n"); |
| gdb_vprintf (gdb_stderr, y, args); |
| } |
| va_end (args); |
| } |
| |
| void _initialize_parse (); |
| void |
| _initialize_parse () |
| { |
| add_setshow_zuinteger_cmd ("expression", class_maintenance, |
| &expressiondebug, |
| _("Set expression debugging."), |
| _("Show expression debugging."), |
| _("When non-zero, the internal representation " |
| "of expressions will be printed."), |
| NULL, |
| show_expressiondebug, |
| &setdebuglist, &showdebuglist); |
| add_setshow_boolean_cmd ("parser", class_maintenance, |
| &parser_debug, |
| _("Set parser debugging."), |
| _("Show parser debugging."), |
| _("When non-zero, expression parser " |
| "tracing will be enabled."), |
| NULL, |
| show_parserdebug, |
| &setdebuglist, &showdebuglist); |
| } |