mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-23 10:03:47 +08:00
40ae603e6e
I searched for spots using ".reset (new ...)" and replaced most of these with std::make_unique. I think this is a bit cleaner and more idiomatic. Regression tested on x86-64 Fedora 40. Reviewed-By: Klaus Gerlicher<klaus.gerlicher@intel.com>
643 lines
18 KiB
C
643 lines
18 KiB
C
/* Parse expressions for GDB.
|
||
|
||
Copyright (C) 1986-2024 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 <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 "cli/cli-cmds.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 = std::make_unique<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
|
||
{
|
||
bound_minimal_symbol msymbol
|
||
= lookup_minimal_symbol (current_program_space, name);
|
||
if (msymbol.minsym != NULL)
|
||
push_new<expr::var_msym_value_operation> (msymbol);
|
||
else if (!have_full_symbols (current_program_space)
|
||
&& !have_partial_symbols (current_program_space))
|
||
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 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 (), nullptr,
|
||
SEARCH_VAR_DOMAIN | SEARCH_FUNCTION_DOMAIN, nullptr);
|
||
if (sym.symbol)
|
||
{
|
||
push_new<expr::var_value_operation> (sym);
|
||
return;
|
||
}
|
||
bound_minimal_symbol msym
|
||
= lookup_minimal_symbol (current_program_space, 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));
|
||
}
|
||
|
||
/* See parser-defs.h. */
|
||
|
||
void
|
||
parser_state::parse_error (const char *msg)
|
||
{
|
||
if (this->prev_lexptr)
|
||
this->lexptr = this->prev_lexptr;
|
||
|
||
if (*this->lexptr == '\0')
|
||
error (_("A %s in expression, near the end of `%s'."),
|
||
msg, this->start_of_input);
|
||
else
|
||
error (_("A %s in expression, near `%s'."), msg, this->lexptr);
|
||
}
|
||
|
||
|
||
|
||
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)
|
||
{
|
||
symtab_and_line cursal
|
||
= get_current_source_symtab_and_line (current_program_space);
|
||
|
||
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 (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 (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);
|
||
}
|