gdb/mi: use separate classes for different types of MI command
authorJan Vrany <jan.vrany@labware.com>
Tue, 23 Jun 2020 13:45:38 +0000 (14:45 +0100)
committerAndrew Burgess <aburgess@redhat.com>
Tue, 14 Dec 2021 11:28:00 +0000 (11:28 +0000)
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
gdb/mi/mi-cmds.h
gdb/mi/mi-main.c
gdb/mi/mi-main.h
gdb/mi/mi-parse.c
gdb/mi/mi-parse.h

index e0eba0bfa67fec552f29694ca8d6363fbadf3aa5..58892fe58ffb711bb43dc7b152119f2c93587328 100644 (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);
-
-  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<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 ()
index e92737be8c840261bfa004576b02773d16bbb491..d405f877e1d417a7be655c93a055d2650c13f7a2 100644 (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;
index 269c01d517613c92b99e7fa0c4c9235ccdf2408b..710eef7e725069bab1f81a13126c17a2c32a1020 100644 (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)
index 7f986969450373297125e766c4e3566f189c0282..064f1d587f223574cbe82026ba5c9102562d5c8d 100644 (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,
index b5b01cf4db02974604bfc1c4e4e95467062cf23e..203024fb01c1d2fcb3cd944d260b737390e75500 100644 (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;
index a60ce5b3a20c3af970be03200cb29a9081bd053f..ceaac72e867c28f58787b8fbee5f4f829d4d7dcf 100644 (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 */