gdb/mi: use separate classes for different types of MI command

This commit changes the infrastructure in mi-cmds.{c,h} to add new
sub-classes for the different types of MI command.  Instances of these
sub-classes are then created and added into mi_cmd_table.

The existing mi_cmd class becomes the abstract base class, this has an
invoke method and takes care of the suppress notifications handling,
before calling a do_invoke virtual method which is implemented by all
of the sub-classes.

There's currently two different sub-classes, one of pure MI commands,
and a second for MI commands that delegate to CLI commands.

There should be no user visible changes after this commit.
This commit is contained in:
Jan Vrany
2020-06-23 14:45:38 +01:00
committed by Andrew Burgess
parent 3be0fed62e
commit 1f6c8c3317
6 changed files with 181 additions and 99 deletions

View File

@ -22,6 +22,7 @@
#include "top.h"
#include "mi-cmds.h"
#include "mi-main.h"
#include "mi-parse.h"
#include <map>
#include <string>
@ -33,6 +34,80 @@ using mi_cmd_up = std::unique_ptr<struct mi_cmd>;
static std::map<std::string, mi_cmd_up> mi_cmd_table;
/* MI command with a pure MI implementation. */
struct mi_command_mi : public mi_cmd
{
/* Constructor. For NAME and SUPPRESS_NOTIFICATION see mi_cmd
constructor, FUNC is the function called from do_invoke, which
implements this MI command. */
mi_command_mi (const char *name, mi_cmd_argv_ftype func,
int *suppress_notification)
: mi_cmd (name, suppress_notification),
m_argv_function (func)
{
gdb_assert (func != nullptr);
}
protected:
/* Called when this MI command has been invoked, calls m_argv_function
with arguments contained within PARSE. */
void do_invoke (struct mi_parse *parse) const override
{
mi_parse_argv (parse->args, parse);
if (parse->argv == nullptr)
error (_("Problem parsing arguments: %s %s"), parse->command,
parse->args);
this->m_argv_function (parse->command, parse->argv, parse->argc);
}
private:
/* The function that implements this MI command. */
mi_cmd_argv_ftype *m_argv_function;
};
/* MI command implemented on top of a CLI command. */
struct mi_command_cli : public mi_cmd
{
/* Constructor. For NAME and SUPPRESS_NOTIFICATION see mi_cmd
constructor, CLI_NAME is the name of a CLI command that should be
invoked to implement this MI command. If ARGS_P is true then any
arguments from entered by the user as part of the MI command line are
forwarded to CLI_NAME as its argument string, otherwise, if ARGS_P is
false, nullptr is send to CLI_NAME as its argument string. */
mi_command_cli (const char *name, const char *cli_name, bool args_p,
int *suppress_notification)
: mi_cmd (name, suppress_notification),
m_cli_name (cli_name),
m_args_p (args_p)
{ /* Nothing. */ }
protected:
/* Called when this MI command has been invoked, calls the m_cli_name
CLI function. In m_args_p is true then the argument string from
within PARSE is passed through to the CLI function, otherwise nullptr
is passed through to the CLI function as its argument string. */
void do_invoke (struct mi_parse *parse) const override
{
const char *args = m_args_p ? parse->args : nullptr;
mi_execute_cli_command (m_cli_name, m_args_p, args);
}
private:
/* The name of the CLI command to execute. */
const char *m_cli_name;
/* Should we be passing an argument string to the m_cli_name function? */
bool m_args_p;
};
/* Insert COMMAND into the global mi_cmd_table. Return false if
COMMAND->name already exists in mi_cmd_table, in which case COMMAND will
not have been added to mi_cmd_table. Otherwise, return true, and
@ -42,9 +117,8 @@ static bool
insert_mi_cmd_entry (mi_cmd_up command)
{
gdb_assert (command != nullptr);
gdb_assert (command->name != nullptr);
std::string name (command->name);
const std::string &name = command->name ();
if (mi_cmd_table.find (name) != mi_cmd_table.end ())
return false;
@ -53,16 +127,6 @@ insert_mi_cmd_entry (mi_cmd_up command)
return true;
}
/* Create an mi_cmd structure with name NAME. */
static mi_cmd_up
create_mi_cmd (const char *name)
{
mi_cmd_up cmd (new mi_cmd ());
cmd->name = name;
return cmd;
}
/* Create and register a new MI command with an MI specific implementation.
NAME must name an MI command that does not already exist, otherwise an
assertion will trigger. */
@ -71,14 +135,10 @@ static void
add_mi_cmd_mi (const char *name, mi_cmd_argv_ftype function,
int *suppress_notification = nullptr)
{
mi_cmd_up cmd_up = create_mi_cmd (name);
mi_cmd_up command (new mi_command_mi (name, function,
suppress_notification));
cmd_up->cli.cmd = nullptr;
cmd_up->cli.args_p = 0;
cmd_up->argv_func = function;
cmd_up->suppress_notification = suppress_notification;
bool success = insert_mi_cmd_entry (std::move (cmd_up));
bool success = insert_mi_cmd_entry (std::move (command));
gdb_assert (success);
}
@ -90,18 +150,44 @@ static void
add_mi_cmd_cli (const char *name, const char *cli_name, int args_p,
int *suppress_notification = nullptr)
{
mi_cmd_up cmd_up = create_mi_cmd (name);
mi_cmd_up command (new mi_command_cli (name, cli_name, args_p != 0,
suppress_notification));
cmd_up->cli.cmd = cli_name;
cmd_up->cli.args_p = args_p;
cmd_up->argv_func = nullptr;
cmd_up->suppress_notification = suppress_notification;
bool success = insert_mi_cmd_entry (std::move (cmd_up));
bool success = insert_mi_cmd_entry (std::move (command));
gdb_assert (success);
}
/* Initialize MI_CMD_TABLE, the global map of MI commands. */
/* See mi-cmds.h. */
mi_cmd::mi_cmd (const char *name, int *suppress_notification)
: m_name (name),
m_suppress_notification (suppress_notification)
{
gdb_assert (m_name != nullptr && m_name[0] != '\0');
}
/* See mi-cmds.h. */
void
mi_cmd::invoke (struct mi_parse *parse) const
{
gdb::optional<scoped_restore_tmpl<int>> restore
= do_suppress_notification ();
this->do_invoke (parse);
}
/* See mi-cmds.h. */
gdb::optional<scoped_restore_tmpl<int>>
mi_cmd::do_suppress_notification () const
{
if (m_suppress_notification != nullptr)
return scoped_restore_tmpl<int> (m_suppress_notification, 1);
else
return {};
}
/* Initialize the available MI commands. */
static void
build_table ()

View File

@ -22,6 +22,8 @@
#ifndef MI_MI_CMDS_H
#define MI_MI_CMDS_H
#include "gdbsupport/gdb_optional.h"
enum print_values {
PRINT_NO_VALUES,
PRINT_ALL_VALUES,
@ -137,37 +139,58 @@ extern mi_cmd_argv_ftype mi_cmd_enable_frame_filters;
extern mi_cmd_argv_ftype mi_cmd_var_set_update_range;
extern mi_cmd_argv_ftype mi_cmd_complete;
/* Description of a single command. */
struct mi_cli
{
/* Corresponding CLI command. If ARGS_P is non-zero, the MI
command's argument list is appended to the CLI command. */
const char *cmd;
int args_p;
};
/* The abstract base class for all MI command types. */
struct mi_cmd
{
/* Official name of the command. */
const char *name;
/* The corresponding CLI command that can be used to implement this
MI command (if cli.lhs is non NULL). */
struct mi_cli cli;
/* If non-null, the function implementing the MI command. */
mi_cmd_argv_ftype *argv_func;
/* If non-null, the pointer to a field in
'struct mi_suppress_notification', which will be set to true by MI
command processor (mi-main.c:mi_cmd_execute) when this command is
being executed. It will be set back to false when command has been
executed. */
int *suppress_notification;
/* Constructor. NAME is the name of this MI command, excluding any
leading dash, that is the initial string the user will enter to run
this command. The SUPPRESS_NOTIFICATION pointer is a flag which will
be set to 1 when this command is invoked, and reset to its previous
value once the command invocation has completed. */
mi_cmd (const char *name, int *suppress_notification);
/* Destructor. */
virtual ~mi_cmd () = default;
/* Return the name of this command. This is the command that the user
will actually type in, without any arguments, and without the leading
dash. */
const char *name () const
{ return m_name; }
/* Execute the MI command. Can throw an exception if something goes
wrong. */
void invoke (struct mi_parse *parse) const;
protected:
/* The core of command invocation, this needs to be overridden in each
base class. PARSE is the parsed command line from the user. */
virtual void do_invoke (struct mi_parse *parse) const = 0;
private:
/* If this command was created with a suppress notifications pointer,
then this function will set the suppress flag and return a
gdb::optional with its value set to an object that will restore the
previous value of the suppress notifications flag.
If this command was created without a suppress notifications points,
then this function returns an empty gdb::optional. */
gdb::optional<scoped_restore_tmpl<int>> do_suppress_notification () const;
/* The name of the command. */
const char *m_name;
/* Pointer to integer to set during command's invocation. */
int *m_suppress_notification;
};
/* Lookup a command in the MI command table, returns nullptr if COMMAND is
not found. */
extern struct mi_cmd *mi_cmd_lookup (const char *command);
extern mi_cmd *mi_cmd_lookup (const char *command);
/* Debug flag */
extern int mi_debug_p;

View File

@ -90,8 +90,6 @@ int mi_proceeded;
static void mi_cmd_execute (struct mi_parse *parse);
static void mi_execute_cli_command (const char *cmd, bool args_p,
const char *args);
static void mi_execute_async_cli_command (const char *cli_command,
char **argv, int argc);
static bool register_changed_p (int regnum, readonly_detached_regcache *,
@ -1936,11 +1934,6 @@ mi_execute_command (const char *cmd, int from_tty)
{
ptid_t previous_ptid = inferior_ptid;
gdb::optional<scoped_restore_tmpl<int>> restore_suppress;
if (command->cmd != NULL && command->cmd->suppress_notification != NULL)
restore_suppress.emplace (command->cmd->suppress_notification, 1);
command->token = token;
if (do_timings)
@ -2079,35 +2072,11 @@ mi_cmd_execute (struct mi_parse *parse)
current_context = parse;
if (parse->cmd->argv_func != NULL)
{
parse->cmd->argv_func (parse->command, parse->argv, parse->argc);
}
else if (parse->cmd->cli.cmd != 0)
{
/* FIXME: DELETE THIS. */
/* The operation is still implemented by a cli command. */
/* Must be a synchronous one. */
bool args_p = parse->cmd->cli.args_p != 0;
const char *args = args_p ? parse->args : nullptr;
mi_execute_cli_command (parse->cmd->cli.cmd, args_p, args);
}
else
{
/* FIXME: DELETE THIS. */
string_file stb;
stb.puts ("Undefined mi command: ");
stb.putstr (parse->command, '"');
stb.puts (" (missing implementation)");
error_stream (stb);
}
gdb_assert (parse->cmd != nullptr);
parse->cmd->invoke (parse);
}
/* FIXME: This is just a hack so we can get some extra commands going.
We don't want to channel things through the CLI, but call libgdb directly.
Use only for synchronous commands. */
/* See mi-main.h. */
void
mi_execute_cli_command (const char *cmd, bool args_p, const char *args)

View File

@ -54,6 +54,18 @@ struct mi_suppress_notification
};
extern struct mi_suppress_notification mi_suppress_notification;
/* This is a hack so we can get some extra commands going, but has existed
within GDB for many years now. Ideally we don't want to channel things
through the CLI, but implement all commands as pure MI commands with
their own implementation.
Execute the CLI command CMD, if ARGS_P is true then ARGS should be a
non-nullptr string containing arguments to add after CMD. If ARGS_P is
false then ARGS must be nullptr. */
extern void mi_execute_cli_command (const char *cmd, bool args_p,
const char *args);
/* Implementation of -fix-multi-location-breakpoint-output. */
extern void mi_cmd_fix_multi_location_breakpoint_output (const char *command,

View File

@ -106,7 +106,7 @@ mi_parse_escape (const char **string_ptr)
return c;
}
static void
void
mi_parse_argv (const char *args, struct mi_parse *parse)
{
const char *chp = args;
@ -363,20 +363,8 @@ mi_parse (const char *cmd, char **token)
chp = skip_spaces (chp);
}
/* For new argv commands, attempt to return the parsed argument
list. */
if (parse->cmd->argv_func != NULL)
{
mi_parse_argv (chp, parse.get ());
if (parse->argv == NULL)
error (_("Problem parsing arguments: %s %s"), parse->command, chp);
}
/* FIXME: DELETE THIS */
/* For CLI commands, also return the remainder of the
command line as a single string. */
if (parse->cmd->cli.cmd != NULL)
parse->args = xstrdup (chp);
/* Save the rest of the arguments for the command. */
parse->args = xstrdup (chp);
/* Fully parsed, flag as an MI command. */
parse->op = MI_COMMAND;

View File

@ -79,4 +79,8 @@ extern std::unique_ptr<struct mi_parse> mi_parse (const char *cmd,
enum print_values mi_parse_print_values (const char *name);
/* Split ARGS into argc/argv and store the result in PARSE. */
extern void mi_parse_argv (const char *args, struct mi_parse *parse);
#endif /* MI_MI_PARSE_H */