From 1f6c8c3317e5692e9994c18f427b45093863d572 Mon Sep 17 00:00:00 2001 From: Jan Vrany Date: Tue, 23 Jun 2020 14:45:38 +0100 Subject: [PATCH] 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. --- gdb/mi/mi-cmds.c | 140 +++++++++++++++++++++++++++++++++++++--------- gdb/mi/mi-cmds.h | 69 +++++++++++++++-------- gdb/mi/mi-main.c | 37 +----------- gdb/mi/mi-main.h | 12 ++++ gdb/mi/mi-parse.c | 18 +----- gdb/mi/mi-parse.h | 4 ++ 6 files changed, 181 insertions(+), 99 deletions(-) diff --git a/gdb/mi/mi-cmds.c b/gdb/mi/mi-cmds.c index e0eba0bfa67..58892fe58ff 100644 --- a/gdb/mi/mi-cmds.c +++ b/gdb/mi/mi-cmds.c @@ -22,6 +22,7 @@ #include "top.h" #include "mi-cmds.h" #include "mi-main.h" +#include "mi-parse.h" #include #include @@ -33,6 +34,80 @@ using mi_cmd_up = std::unique_ptr; static std::map 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); - - cmd_up->cli.cmd = nullptr; - cmd_up->cli.args_p = 0; - cmd_up->argv_func = function; - cmd_up->suppress_notification = suppress_notification; + mi_cmd_up command (new mi_command_mi (name, function, + 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); - - cmd_up->cli.cmd = cli_name; - cmd_up->cli.args_p = args_p; - cmd_up->argv_func = nullptr; - cmd_up->suppress_notification = suppress_notification; + mi_cmd_up command (new mi_command_cli (name, cli_name, args_p != 0, + 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> restore + = do_suppress_notification (); + this->do_invoke (parse); +} + +/* See mi-cmds.h. */ + +gdb::optional> +mi_cmd::do_suppress_notification () const +{ + if (m_suppress_notification != nullptr) + return scoped_restore_tmpl (m_suppress_notification, 1); + else + return {}; +} + +/* Initialize the available MI commands. */ static void build_table () diff --git a/gdb/mi/mi-cmds.h b/gdb/mi/mi-cmds.h index e92737be8c8..d405f877e1d 100644 --- a/gdb/mi/mi-cmds.h +++ b/gdb/mi/mi-cmds.h @@ -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> 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; diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c index 269c01d5176..710eef7e725 100644 --- a/gdb/mi/mi-main.c +++ b/gdb/mi/mi-main.c @@ -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> 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) diff --git a/gdb/mi/mi-main.h b/gdb/mi/mi-main.h index 7f986969450..064f1d587f2 100644 --- a/gdb/mi/mi-main.h +++ b/gdb/mi/mi-main.h @@ -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, diff --git a/gdb/mi/mi-parse.c b/gdb/mi/mi-parse.c index b5b01cf4db0..203024fb01c 100644 --- a/gdb/mi/mi-parse.c +++ b/gdb/mi/mi-parse.c @@ -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; diff --git a/gdb/mi/mi-parse.h b/gdb/mi/mi-parse.h index a60ce5b3a20..ceaac72e867 100644 --- a/gdb/mi/mi-parse.h +++ b/gdb/mi/mi-parse.h @@ -79,4 +79,8 @@ extern std::unique_ptr 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 */ -- 2.30.2