public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH] MI: Add new command -complete
@ 2019-01-03 22:30 Jan Vrany
  2019-01-16  9:21 ` Jan Vrany
  2019-01-17 20:29 ` Tom Tromey
  0 siblings, 2 replies; 50+ messages in thread
From: Jan Vrany @ 2019-01-03 22:30 UTC (permalink / raw)
  To: gdb-patches; +Cc: Jan Vrany

There is a CLI command 'complete' intended to use with emacs. Such a command
would also be useful for MI frontends, when separate CLI and MI channels cannot
be used. For example, on Windows (because of lack of PTYs) or when GDB is used
through SSH session.

This commit adds a new '-complete' MI command which is implemented using
CLI's 'complete'.

gdb/Changelog:
2019-01-03  Jan Vrany  <jan.vrany@fit.cvut.cz>

	* cli/cli-cmds.h: Export complete_command.
	* cli/cli-cmds.c (complete_command): Update to print completions
	on MI channel if requested.
	* mi/mi-cmds.c: Define new -complete command implemented using
	it CLI command 'complete'.
	* NEWS: Mention new -complete command.

gdb/doc/ChangeLog:
2019-01-03  Jan Vrany  <jan.vrany@fit.cvut.cz>

	* gdb.texinfo (Miscellaneous GDB/MI Commands): Document new
	MI command -complete.

gdb/testsuite/ChangeLog:
2019-01-03  Jan Vrany  <jan.vrany@fit.cvut.cz>

	* gdb.mi/mi-complete.exp: New file.
---
 gdb/ChangeLog                        |  9 +++
 gdb/NEWS                             |  7 ++
 gdb/cli/cli-cmds.c                   | 95 +++++++++++++++++++---------
 gdb/cli/cli-cmds.h                   |  4 ++
 gdb/doc/ChangeLog                    |  5 ++
 gdb/doc/gdb.texinfo                  | 32 ++++++++++
 gdb/mi/mi-cmds.c                     |  3 +
 gdb/testsuite/ChangeLog              |  4 ++
 gdb/testsuite/gdb.mi/mi-complete.exp | 71 +++++++++++++++++++++
 9 files changed, 199 insertions(+), 31 deletions(-)
 create mode 100644 gdb/testsuite/gdb.mi/mi-complete.exp

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 08c7c6c1f4..322b539417 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,12 @@
+2019-01-03  Jan Vrany  <jan.vrany@fit.cvut.cz>
+
+	* cli/cli-cmds.h: Export complete_command.
+	* cli/cli-cmds.c (complete_command): Update to print completions
+	on MI channel if requested.
+	* mi/mi-cmds.c: Define new -complete command implemented using
+	it CLI command 'complete'.
+	* NEWS: Mention new -complete command.
+
 2018-12-17  Andrew Burgess  <andrew.burgess@embecosm.com>
 
 	* dwarf2read.c (struct dwarf2_cu): Convert the fields 'mark',
diff --git a/gdb/NEWS b/gdb/NEWS
index 913f3ae3b6..6b561af71f 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -96,6 +96,13 @@ maint show dwarf unwinders
 info proc files
   Display a list of open files for a process.
 
+* New MI commands
+
+-complete
+  This lists all the possible completions for the rest of the line, if it
+  were to be given as a command itself.  This is intended for use by MI frontends
+  in cases when separate CLI and MI channels cannot be used.
+
 * Changed commands
 
 target remote FILENAME
diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index 135f550b80..468f66dac6 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -223,16 +223,22 @@ help_command (const char *command, int from_tty)
 /* Note: The "complete" command is used by Emacs to implement completion.
    [Is that why this function writes output with *_unfiltered?]  */
 
-static void
+void
 complete_command (const char *arg, int from_tty)
 {
+  struct ui_out *uiout = current_uiout;
+  bool is_mi_like = uiout->is_mi_like_p ();
+
   dont_repeat ();
 
   if (max_completions == 0)
     {
-      /* Only print this for non-mi frontends.  An MI frontend may not
-	 be able to handle this.  */
-      if (!current_uiout->is_mi_like_p ())
+      if (is_mi_like)
+        {
+          error (_("max-completions is zero,"
+		   " completion is disabled.\n"));
+        }
+      else
 	{
 	  printf_unfiltered (_("max-completions is zero,"
 			       " completion is disabled.\n"));
@@ -278,34 +284,61 @@ complete_command (const char *arg, int from_tty)
   completion_result result
     = tracker->build_completion_result (word, word - arg, strlen (arg));
 
-  if (result.number_matches != 0)
-    {
-      if (result.number_matches == 1)
-	printf_unfiltered ("%s%s\n", arg_prefix.c_str (), result.match_list[0]);
-      else
-	{
-	  result.sort_match_list ();
-
-	  for (size_t i = 0; i < result.number_matches; i++)
-	    {
-	      printf_unfiltered ("%s%s",
-				 arg_prefix.c_str (),
-				 result.match_list[i + 1]);
-	      if (quote_char)
-		printf_unfiltered ("%c", quote_char);
-	      printf_unfiltered ("\n");
-	    }
-	}
-
-      if (result.number_matches == max_completions)
-	{
-	  /* ARG_PREFIX and WORD are included in the output so that emacs
-	     will include the message in the output.  */
-	  printf_unfiltered (_("%s%s %s\n"),
-			     arg_prefix.c_str (), word,
-			     get_max_completions_reached_message ());
-	}
+  {
+    ui_out_emit_list completions_emitter (uiout, "completions");
+    if (result.number_matches != 0)
+      {
+        if (result.number_matches == 1)
+          {
+	    if (is_mi_like)
+	      uiout->field_fmt(NULL, "%s%s", arg_prefix.c_str (),
+                                             result.match_list[0]);
+	    else
+	      printf_unfiltered ("%s%s\n", arg_prefix.c_str (),
+                                           result.match_list[0]);
+          }
+        else
+	  {
+	    result.sort_match_list ();
+
+	    for (size_t i = 0; i < result.number_matches; i++)
+	      {
+                if (is_mi_like)
+	          {
+	            uiout->field_fmt(NULL, "%s%s", arg_prefix.c_str (),
+                                                   result.match_list[i + 1]);
+	          }
+	        else
+	          {
+		    printf_unfiltered ("%s%s", arg_prefix.c_str (),
+                                               result.match_list[i + 1]);
+		    if (quote_char)
+		      printf_unfiltered ("%c", quote_char);
+		    printf_unfiltered ("\n");
+	          }
+	      }
+	  }
+      }
     }
+
+    if (result.number_matches == max_completions)
+      {
+	if (is_mi_like)
+	  uiout->field_string("max_completions_reached", "1");
+	else
+	  {
+	    /* ARG_PREFIX and WORD are included in the output so that emacs
+	       will include the message in the output.  */
+	    printf_unfiltered (_("%s%s %s\n"),
+				 arg_prefix.c_str (), word,
+				 get_max_completions_reached_message ());
+	  }
+      }
+    else
+      {
+	if (is_mi_like)
+	  uiout->field_string("max_completions_reached", "0");
+      }
 }
 
 int
diff --git a/gdb/cli/cli-cmds.h b/gdb/cli/cli-cmds.h
index 3fa188dba6..bcd12c0c0b 100644
--- a/gdb/cli/cli-cmds.h
+++ b/gdb/cli/cli-cmds.h
@@ -106,6 +106,10 @@ void init_cmd_lists (void);
 
 void init_cli_cmds (void);
 
+/* Exported to gdb/mi/mi-cmds.c */
+
+void complete_command (const char *, int);
+
 int is_complete_command (struct cmd_list_element *cmd);
 
 /* Exported to gdb/main.c */
diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog
index 68d3068c72..da81f6a6c1 100644
--- a/gdb/doc/ChangeLog
+++ b/gdb/doc/ChangeLog
@@ -1,3 +1,8 @@
+2019-01-03  Jan Vrany  <jan.vrany@fit.cvut.cz>
+
+	* gdb.texinfo (Miscellaneous GDB/MI Commands): Document new
+	MI command -complete.
+
 2018-12-13  John Baldwin  <jhb@FreeBSD.org>
 
 	* gdb.texinfo (Set Catchpoints): Add an anchor for 'catch syscall'.
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index d766e44e63..7f88273145 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -34236,6 +34236,38 @@ fullname="/home/nickrob/myprog.c",line="73",arch="i386:x86_64"@}
 (gdb)
 @end smallexample
 
+@subheading The @code{-complete} Command
+@findex -complete
+
+@subheading Synopsis
+
+@smallexample
+-complete @var{command}
+@end smallexample
+
+Show a list of completions for partially typed CLI @var{command}. The
+@var{command} should be given in verbatim without any quoting / escaping.
+
+This command is intended for @sc{gdb/mi} frontends that cannot use two separate
+CLI and MI channels - for example: because of lack of PTYs like on Windows or
+because @sc{gdb} is used remotely via a SSH connection.
+
+@subheading @value{GDBN} Command
+
+The corresponding @value{GDBN} command is @samp{complete}.
+
+@subheading Example
+
+@smallexample
+(gdb)
+-complete br
+^done,completions=["break","break-range"],max_completions_reached="0"
+(gdb)
+-complete b madv
+^done,completions=["b madvise"],max_completions_reached="0"
+(gdb)
+@end smallexample
+
 @node Annotations
 @chapter @value{GDBN} Annotations
 
diff --git a/gdb/mi/mi-cmds.c b/gdb/mi/mi-cmds.c
index 51014eded2..7b510f71d4 100644
--- a/gdb/mi/mi-cmds.c
+++ b/gdb/mi/mi-cmds.c
@@ -20,9 +20,11 @@
 
 #include "defs.h"
 #include "top.h"
+#include "cli/cli-cmds.h"
 #include "mi-cmds.h"
 #include "mi-main.h"
 
+
 struct mi_cmd;
 static struct mi_cmd **lookup_table (const char *command);
 static void build_table (struct mi_cmd *commands);
@@ -75,6 +77,7 @@ static struct mi_cmd mi_cmds[] =
                    &mi_suppress_notification.breakpoint),
   DEF_MI_CMD_MI_1 ("catch-unload", mi_cmd_catch_unload,
                    &mi_suppress_notification.breakpoint),
+  DEF_MI_CMD_CLI ("complete", "complete", 1),
   DEF_MI_CMD_MI ("data-disassemble", mi_cmd_disassemble),
   DEF_MI_CMD_MI ("data-evaluate-expression", mi_cmd_data_evaluate_expression),
   DEF_MI_CMD_MI ("data-list-changed-registers",
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index ab3a74fffd..40da71b2c5 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,3 +1,7 @@
+2019-01-03  Jan Vrany  <jan.vrany@fit.cvut.cz>
+
+	* gdb.mi/mi-complete.exp: New file.
+
 2018-12-12  Andrew Burgess  <andrew.burgess@embecosm.com>
 
 	* gdb.base/annota1.exp: Update a test regexp.
diff --git a/gdb/testsuite/gdb.mi/mi-complete.exp b/gdb/testsuite/gdb.mi/mi-complete.exp
new file mode 100644
index 0000000000..14c763a966
--- /dev/null
+++ b/gdb/testsuite/gdb.mi/mi-complete.exp
@@ -0,0 +1,71 @@
+# Copyright 2018 Free Software Foundation, Inc.
+
+# 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/>.
+
+# Verify -data-evaluate-expression. There are really minimal tests.
+
+# The goal is not to test gdb functionality, which is done by other tests,
+# but to verify the correct output response to MI operations.
+#
+
+load_lib mi-support.exp
+set MIFLAGS "-i=mi"
+
+gdb_exit
+if [mi_gdb_start] {
+    continue
+}
+
+standard_testfile basics.c
+
+if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+     untested "failed to compile"
+     return -1
+}
+
+mi_run_to_main
+
+mi_gdb_test "1-complete br" \
+            "1\\^done,completions=\\\[.*\"break\",.*\"break-range\".*\\\],max_completions_reached=\"0\"" \
+            "-complete br"
+
+# Check empty completion list
+mi_gdb_test "5-complete bogus" \
+            "5\\^done,completions=\\\[\\\],max_completions_reached=\"0\"" \
+            "-complete bogus"
+
+
+# Check completions for commands with space
+mi_gdb_test "4-complete b mai" \
+            "4\\^done,completions=\\\[.*\"b main\".*\\\],max_completions_reached=\"0\"" \
+            "-complete b mai"
+
+mi_gdb_test "-info-gdb-mi-command complete" \
+            "\\^done,command=\{exists=\"true\"\}" \
+            "-info-gdb-mi-command complete"
+
+# Limit max completions and check that max_completions_reached=\"0\" is set
+# to 1.
+send_gdb "set max-completions 1\n"
+
+mi_gdb_test "2-complete br" \
+            ".*2\\^done,completions=\\\[\"br\[A-Za-z0-9-\]+\"\\\],max_completions_reached=\"1\"" \
+            "-complete br (max-completions 1)"
+
+# Disable completions and check an error is returned
+send_gdb "set max-completions 0\n"
+
+mi_gdb_test "3-complete br" \
+            ".*3\\^error,msg=\".*" \
+            "-complete br (max-completions 1)"
-- 
2.20.1

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH] MI: Add new command -complete
  2019-01-03 22:30 [PATCH] MI: Add new command -complete Jan Vrany
@ 2019-01-16  9:21 ` Jan Vrany
  2019-01-17 20:29 ` Tom Tromey
  1 sibling, 0 replies; 50+ messages in thread
From: Jan Vrany @ 2019-01-16  9:21 UTC (permalink / raw)
  To: gdb-patches

Polite ping. 

Jan

On Thu, 2019-01-03 at 22:29 +0000, Jan Vrany wrote:
> There is a CLI command 'complete' intended to use with emacs. Such a command
> would also be useful for MI frontends, when separate CLI and MI channels cannot
> be used. For example, on Windows (because of lack of PTYs) or when GDB is used
> through SSH session.
> 
> This commit adds a new '-complete' MI command which is implemented using
> CLI's 'complete'.
> 
> gdb/Changelog:
> 2019-01-03  Jan Vrany  <jan.vrany@fit.cvut.cz>
> 
> 	* cli/cli-cmds.h: Export complete_command.
> 	* cli/cli-cmds.c (complete_command): Update to print completions
> 	on MI channel if requested.
> 	* mi/mi-cmds.c: Define new -complete command implemented using
> 	it CLI command 'complete'.
> 	* NEWS: Mention new -complete command.
> 
> gdb/doc/ChangeLog:
> 2019-01-03  Jan Vrany  <jan.vrany@fit.cvut.cz>
> 
> 	* gdb.texinfo (Miscellaneous GDB/MI Commands): Document new
> 	MI command -complete.
> 
> gdb/testsuite/ChangeLog:
> 2019-01-03  Jan Vrany  <jan.vrany@fit.cvut.cz>
> 
> 	* gdb.mi/mi-complete.exp: New file.
> ---
>  gdb/ChangeLog                        |  9 +++
>  gdb/NEWS                             |  7 ++
>  gdb/cli/cli-cmds.c                   | 95 +++++++++++++++++++---------
>  gdb/cli/cli-cmds.h                   |  4 ++
>  gdb/doc/ChangeLog                    |  5 ++
>  gdb/doc/gdb.texinfo                  | 32 ++++++++++
>  gdb/mi/mi-cmds.c                     |  3 +
>  gdb/testsuite/ChangeLog              |  4 ++
>  gdb/testsuite/gdb.mi/mi-complete.exp | 71 +++++++++++++++++++++
>  9 files changed, 199 insertions(+), 31 deletions(-)
>  create mode 100644 gdb/testsuite/gdb.mi/mi-complete.exp
> 
> diff --git a/gdb/ChangeLog b/gdb/ChangeLog
> index 08c7c6c1f4..322b539417 100644
> --- a/gdb/ChangeLog
> +++ b/gdb/ChangeLog
> @@ -1,3 +1,12 @@
> +2019-01-03  Jan Vrany  <jan.vrany@fit.cvut.cz>
> +
> +	* cli/cli-cmds.h: Export complete_command.
> +	* cli/cli-cmds.c (complete_command): Update to print completions
> +	on MI channel if requested.
> +	* mi/mi-cmds.c: Define new -complete command implemented using
> +	it CLI command 'complete'.
> +	* NEWS: Mention new -complete command.
> +
>  2018-12-17  Andrew Burgess  <andrew.burgess@embecosm.com>
>  
>  	* dwarf2read.c (struct dwarf2_cu): Convert the fields 'mark',
> diff --git a/gdb/NEWS b/gdb/NEWS
> index 913f3ae3b6..6b561af71f 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -96,6 +96,13 @@ maint show dwarf unwinders
>  info proc files
>    Display a list of open files for a process.
>  
> +* New MI commands
> +
> +-complete
> +  This lists all the possible completions for the rest of the line, if it
> +  were to be given as a command itself.  This is intended for use by MI frontends
> +  in cases when separate CLI and MI channels cannot be used.
> +
>  * Changed commands
>  
>  target remote FILENAME
> diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
> index 135f550b80..468f66dac6 100644
> --- a/gdb/cli/cli-cmds.c
> +++ b/gdb/cli/cli-cmds.c
> @@ -223,16 +223,22 @@ help_command (const char *command, int from_tty)
>  /* Note: The "complete" command is used by Emacs to implement completion.
>     [Is that why this function writes output with *_unfiltered?]  */
>  
> -static void
> +void
>  complete_command (const char *arg, int from_tty)
>  {
> +  struct ui_out *uiout = current_uiout;
> +  bool is_mi_like = uiout->is_mi_like_p ();
> +
>    dont_repeat ();
>  
>    if (max_completions == 0)
>      {
> -      /* Only print this for non-mi frontends.  An MI frontend may not
> -	 be able to handle this.  */
> -      if (!current_uiout->is_mi_like_p ())
> +      if (is_mi_like)
> +        {
> +          error (_("max-completions is zero,"
> +		   " completion is disabled.\n"));
> +        }
> +      else
>  	{
>  	  printf_unfiltered (_("max-completions is zero,"
>  			       " completion is disabled.\n"));
> @@ -278,34 +284,61 @@ complete_command (const char *arg, int from_tty)
>    completion_result result
>      = tracker->build_completion_result (word, word - arg, strlen (arg));
>  
> -  if (result.number_matches != 0)
> -    {
> -      if (result.number_matches == 1)
> -	printf_unfiltered ("%s%s\n", arg_prefix.c_str (), result.match_list[0]);
> -      else
> -	{
> -	  result.sort_match_list ();
> -
> -	  for (size_t i = 0; i < result.number_matches; i++)
> -	    {
> -	      printf_unfiltered ("%s%s",
> -				 arg_prefix.c_str (),
> -				 result.match_list[i + 1]);
> -	      if (quote_char)
> -		printf_unfiltered ("%c", quote_char);
> -	      printf_unfiltered ("\n");
> -	    }
> -	}
> -
> -      if (result.number_matches == max_completions)
> -	{
> -	  /* ARG_PREFIX and WORD are included in the output so that emacs
> -	     will include the message in the output.  */
> -	  printf_unfiltered (_("%s%s %s\n"),
> -			     arg_prefix.c_str (), word,
> -			     get_max_completions_reached_message ());
> -	}
> +  {
> +    ui_out_emit_list completions_emitter (uiout, "completions");
> +    if (result.number_matches != 0)
> +      {
> +        if (result.number_matches == 1)
> +          {
> +	    if (is_mi_like)
> +	      uiout->field_fmt(NULL, "%s%s", arg_prefix.c_str (),
> +                                             result.match_list[0]);
> +	    else
> +	      printf_unfiltered ("%s%s\n", arg_prefix.c_str (),
> +                                           result.match_list[0]);
> +          }
> +        else
> +	  {
> +	    result.sort_match_list ();
> +
> +	    for (size_t i = 0; i < result.number_matches; i++)
> +	      {
> +                if (is_mi_like)
> +	          {
> +	            uiout->field_fmt(NULL, "%s%s", arg_prefix.c_str (),
> +                                                   result.match_list[i + 1]);
> +	          }
> +	        else
> +	          {
> +		    printf_unfiltered ("%s%s", arg_prefix.c_str (),
> +                                               result.match_list[i + 1]);
> +		    if (quote_char)
> +		      printf_unfiltered ("%c", quote_char);
> +		    printf_unfiltered ("\n");
> +	          }
> +	      }
> +	  }
> +      }
>      }
> +
> +    if (result.number_matches == max_completions)
> +      {
> +	if (is_mi_like)
> +	  uiout->field_string("max_completions_reached", "1");
> +	else
> +	  {
> +	    /* ARG_PREFIX and WORD are included in the output so that emacs
> +	       will include the message in the output.  */
> +	    printf_unfiltered (_("%s%s %s\n"),
> +				 arg_prefix.c_str (), word,
> +				 get_max_completions_reached_message ());
> +	  }
> +      }
> +    else
> +      {
> +	if (is_mi_like)
> +	  uiout->field_string("max_completions_reached", "0");
> +      }
>  }
>  
>  int
> diff --git a/gdb/cli/cli-cmds.h b/gdb/cli/cli-cmds.h
> index 3fa188dba6..bcd12c0c0b 100644
> --- a/gdb/cli/cli-cmds.h
> +++ b/gdb/cli/cli-cmds.h
> @@ -106,6 +106,10 @@ void init_cmd_lists (void);
>  
>  void init_cli_cmds (void);
>  
> +/* Exported to gdb/mi/mi-cmds.c */
> +
> +void complete_command (const char *, int);
> +
>  int is_complete_command (struct cmd_list_element *cmd);
>  
>  /* Exported to gdb/main.c */
> diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog
> index 68d3068c72..da81f6a6c1 100644
> --- a/gdb/doc/ChangeLog
> +++ b/gdb/doc/ChangeLog
> @@ -1,3 +1,8 @@
> +2019-01-03  Jan Vrany  <jan.vrany@fit.cvut.cz>
> +
> +	* gdb.texinfo (Miscellaneous GDB/MI Commands): Document new
> +	MI command -complete.
> +
>  2018-12-13  John Baldwin  <jhb@FreeBSD.org>
>  
>  	* gdb.texinfo (Set Catchpoints): Add an anchor for 'catch syscall'.
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index d766e44e63..7f88273145 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -34236,6 +34236,38 @@ fullname="/home/nickrob/myprog.c",line="73",arch="i386:x86_64"@}
>  (gdb)
>  @end smallexample
>  
> +@subheading The @code{-complete} Command
> +@findex -complete
> +
> +@subheading Synopsis
> +
> +@smallexample
> +-complete @var{command}
> +@end smallexample
> +
> +Show a list of completions for partially typed CLI @var{command}. The
> +@var{command} should be given in verbatim without any quoting / escaping.
> +
> +This command is intended for @sc{gdb/mi} frontends that cannot use two separate
> +CLI and MI channels - for example: because of lack of PTYs like on Windows or
> +because @sc{gdb} is used remotely via a SSH connection.
> +
> +@subheading @value{GDBN} Command
> +
> +The corresponding @value{GDBN} command is @samp{complete}.
> +
> +@subheading Example
> +
> +@smallexample
> +(gdb)
> +-complete br
> +^done,completions=["break","break-range"],max_completions_reached="0"
> +(gdb)
> +-complete b madv
> +^done,completions=["b madvise"],max_completions_reached="0"
> +(gdb)
> +@end smallexample
> +
>  @node Annotations
>  @chapter @value{GDBN} Annotations
>  
> diff --git a/gdb/mi/mi-cmds.c b/gdb/mi/mi-cmds.c
> index 51014eded2..7b510f71d4 100644
> --- a/gdb/mi/mi-cmds.c
> +++ b/gdb/mi/mi-cmds.c
> @@ -20,9 +20,11 @@
>  
>  #include "defs.h"
>  #include "top.h"
> +#include "cli/cli-cmds.h"
>  #include "mi-cmds.h"
>  #include "mi-main.h"
>  
> +
>  struct mi_cmd;
>  static struct mi_cmd **lookup_table (const char *command);
>  static void build_table (struct mi_cmd *commands);
> @@ -75,6 +77,7 @@ static struct mi_cmd mi_cmds[] =
>                     &mi_suppress_notification.breakpoint),
>    DEF_MI_CMD_MI_1 ("catch-unload", mi_cmd_catch_unload,
>                     &mi_suppress_notification.breakpoint),
> +  DEF_MI_CMD_CLI ("complete", "complete", 1),
>    DEF_MI_CMD_MI ("data-disassemble", mi_cmd_disassemble),
>    DEF_MI_CMD_MI ("data-evaluate-expression", mi_cmd_data_evaluate_expression),
>    DEF_MI_CMD_MI ("data-list-changed-registers",
> diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
> index ab3a74fffd..40da71b2c5 100644
> --- a/gdb/testsuite/ChangeLog
> +++ b/gdb/testsuite/ChangeLog
> @@ -1,3 +1,7 @@
> +2019-01-03  Jan Vrany  <jan.vrany@fit.cvut.cz>
> +
> +	* gdb.mi/mi-complete.exp: New file.
> +
>  2018-12-12  Andrew Burgess  <andrew.burgess@embecosm.com>
>  
>  	* gdb.base/annota1.exp: Update a test regexp.
> diff --git a/gdb/testsuite/gdb.mi/mi-complete.exp b/gdb/testsuite/gdb.mi/mi-complete.exp
> new file mode 100644
> index 0000000000..14c763a966
> --- /dev/null
> +++ b/gdb/testsuite/gdb.mi/mi-complete.exp
> @@ -0,0 +1,71 @@
> +# Copyright 2018 Free Software Foundation, Inc.
> +
> +# 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/>;.
> +
> +# Verify -data-evaluate-expression. There are really minimal tests.
> +
> +# The goal is not to test gdb functionality, which is done by other tests,
> +# but to verify the correct output response to MI operations.
> +#
> +
> +load_lib mi-support.exp
> +set MIFLAGS "-i=mi"
> +
> +gdb_exit
> +if [mi_gdb_start] {
> +    continue
> +}
> +
> +standard_testfile basics.c
> +
> +if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
> +     untested "failed to compile"
> +     return -1
> +}
> +
> +mi_run_to_main
> +
> +mi_gdb_test "1-complete br" \
> +            "1\\^done,completions=\\\[.*\"break\",.*\"break-range\".*\\\],max_completions_reached=\"0\"" \
> +            "-complete br"
> +
> +# Check empty completion list
> +mi_gdb_test "5-complete bogus" \
> +            "5\\^done,completions=\\\[\\\],max_completions_reached=\"0\"" \
> +            "-complete bogus"
> +
> +
> +# Check completions for commands with space
> +mi_gdb_test "4-complete b mai" \
> +            "4\\^done,completions=\\\[.*\"b main\".*\\\],max_completions_reached=\"0\"" \
> +            "-complete b mai"
> +
> +mi_gdb_test "-info-gdb-mi-command complete" \
> +            "\\^done,command=\{exists=\"true\"\}" \
> +            "-info-gdb-mi-command complete"
> +
> +# Limit max completions and check that max_completions_reached=\"0\" is set
> +# to 1.
> +send_gdb "set max-completions 1\n"
> +
> +mi_gdb_test "2-complete br" \
> +            ".*2\\^done,completions=\\\[\"br\[A-Za-z0-9-\]+\"\\\],max_completions_reached=\"1\"" \
> +            "-complete br (max-completions 1)"
> +
> +# Disable completions and check an error is returned
> +send_gdb "set max-completions 0\n"
> +
> +mi_gdb_test "3-complete br" \
> +            ".*3\\^error,msg=\".*" \
> +            "-complete br (max-completions 1)"

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH] MI: Add new command -complete
  2019-01-03 22:30 [PATCH] MI: Add new command -complete Jan Vrany
  2019-01-16  9:21 ` Jan Vrany
@ 2019-01-17 20:29 ` Tom Tromey
  2019-01-17 21:01   ` Jan Vrany
  2019-01-28 12:41   ` [PATCH v2 0/2] " Jan Vrany
  1 sibling, 2 replies; 50+ messages in thread
From: Tom Tromey @ 2019-01-17 20:29 UTC (permalink / raw)
  To: Jan Vrany; +Cc: gdb-patches

>>>>> "Jan" == Jan Vrany <jan.vrany@fit.cvut.cz> writes:

Jan> This commit adds a new '-complete' MI command which is implemented using
Jan> CLI's 'complete'.

I suspect it would be better to just write a new MI function and then
mildly refactor complete_command so that the two commands can share most
of their implementation.

First, this would make it so we could add options to the MI command as
needed.  (Perhaps the current argument to the command ought to have a
flag to avoid future parsing problems?  I am not sure.)

Second, it would mean that we could make changes to the CLI command
without worrying about breaking MI compatibility.

What do you think of this?

I looked and I see that some other MI commands are implemented directly
by CLI commands.  I am not sure but I suspect this might be a deprecated
approach.

Jan> +  bool is_mi_like = uiout->is_mi_like_p ();

This could be removed by having a more explicit API.

Jan> +    ui_out_emit_list completions_emitter (uiout, "completions");
Jan> +    if (result.number_matches != 0)
Jan> +      {
Jan> +        if (result.number_matches == 1)
Jan> +          {
Jan> +	    if (is_mi_like)
Jan> +	      uiout->field_fmt(NULL, "%s%s", arg_prefix.c_str (),
Jan> +                                             result.match_list[0]);
Jan> +	    else
Jan> +	      printf_unfiltered ("%s%s\n", arg_prefix.c_str (),
Jan> +                                           result.match_list[0]);

Space before "(" in the field_fmt call.  Unifying the cases would be
preferable.  Or another argument for separation.

thanks,
Tom

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH] MI: Add new command -complete
  2019-01-17 20:29 ` Tom Tromey
@ 2019-01-17 21:01   ` Jan Vrany
  2019-01-28 12:41   ` [PATCH v2 0/2] " Jan Vrany
  1 sibling, 0 replies; 50+ messages in thread
From: Jan Vrany @ 2019-01-17 21:01 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

On Thu, 2019-01-17 at 13:29 -0700, Tom Tromey wrote:
> > > > > > "Jan" == Jan Vrany <jan.vrany@fit.cvut.cz> writes:
> 
> Jan> This commit adds a new '-complete' MI command which is implemented using
> Jan> CLI's 'complete'.
> 
> I suspect it would be better to just write a new MI function and then
> mildly refactor complete_command so that the two commands can share most
> of their implementation.
> 
> First, this would make it so we could add options to the MI command as
> needed.  (Perhaps the current argument to the command ought to have a
> flag to avoid future parsing problems?  

What do you mean by this? Can you give an example? 

> I am not sure.)
> 
> Second, it would mean that we could make changes to the CLI command
> without worrying about breaking MI compatibility.

As for changes to CLI command, I've got the impression from the code that
the format is rather strict (from use of printf_unfiltered(), for instance),
deliberately so to not break clients (Emacs is mentioned). Indeed, not really
an argument against splitting. 

> 
> What do you think of this?

I thought of that actually. Also, it'd make the MI interface more regular
w.r.t quoting of spaces and other special characters. But in the end I could 
not find a way without duplicating enough code from the original function, 
so hacked it that way. 

Let me try harder :-) 

> 
> I looked and I see that some other MI commands are implemented directly
> by CLI commands.  I am not sure but I suspect this might be a deprecated
> approach.

Yeah, there's quite a lot of them. If it is a deprecated approach, 
I did not know. 

> 
> Jan> +  bool is_mi_like = uiout->is_mi_like_p ();
> 
> This could be removed by having a more explicit API.

I'm sorry, I'm not sure what do you mean here. 

> 
> Jan> +    ui_out_emit_list completions_emitter (uiout, "completions");
> Jan> +    if (result.number_matches != 0)
> Jan> +      {
> Jan> +        if (result.number_matches == 1)
> Jan> +          {
> Jan> +	    if (is_mi_like)
> Jan> +	      uiout->field_fmt(NULL, "%s%s", arg_prefix.c_str (),
> Jan> +                                             result.match_list[0]);
> Jan> +	    else
> Jan> +	      printf_unfiltered ("%s%s\n", arg_prefix.c_str (),
> Jan> +                                           result.match_list[0]);
> 
> Space before "(" in the field_fmt call.  Unifying the cases would be
> preferable.  Or another argument for separation.
> 

Thanks! 
Jan

^ permalink raw reply	[flat|nested] 50+ messages in thread

* [PATCH v2 2/2] MI: Add new command -complete
  2019-01-28 12:41   ` [PATCH v2 0/2] " Jan Vrany
@ 2019-01-28 12:41     ` Jan Vrany
  2019-01-28 12:41     ` [PATCH v2 1/2] MI: extract command completion logic from complete_command() Jan Vrany
                       ` (14 subsequent siblings)
  15 siblings, 0 replies; 50+ messages in thread
From: Jan Vrany @ 2019-01-28 12:41 UTC (permalink / raw)
  To: gdb-patches; +Cc: Jan Vrany

There is a CLI command 'complete' intended to use with emacs. Such a command
would also be useful for MI frontends, when separate CLI and MI channels cannot
be used. For example, on Windows (because of lack of PTYs) or when GDB is used
through SSH session.

This commit adds a new '-complete' MI command.

gdb/Changelog:
2019-01-28  Jan Vrany  <jan.vrany@fit.cvut.cz>

	* mi/mi-cmds.h (mi_cmd_complete): New function.
	* mi/mi-main.c (mi_cmd_complete): Likewise.
	* mi/mi-cmds.c: Define new MI command -complete.
	* NEWS: Mention new -complete command.

gdb/doc/ChangeLog:
2019-01-28  Jan Vrany  <jan.vrany@fit.cvut.cz>

	* gdb.texinfo (Miscellaneous GDB/MI Commands): Document new
	MI command -complete.

gdb/testsuite/ChangeLog:
2019-01-28  Jan Vrany  <jan.vrany@fit.cvut.cz>

	* gdb.mi/mi-complete.exp: New file.
---
 gdb/ChangeLog                        |  7 +++
 gdb/NEWS                             |  7 +++
 gdb/doc/ChangeLog                    |  5 ++
 gdb/doc/gdb.texinfo                  | 31 ++++++++++++
 gdb/mi/mi-cmds.c                     |  2 +
 gdb/mi/mi-cmds.h                     |  1 +
 gdb/mi/mi-main.c                     | 44 +++++++++++++++++
 gdb/testsuite/ChangeLog              |  4 ++
 gdb/testsuite/gdb.mi/mi-complete.exp | 71 ++++++++++++++++++++++++++++
 9 files changed, 172 insertions(+)
 create mode 100644 gdb/testsuite/gdb.mi/mi-complete.exp

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 17e29a7807..51e62a7d30 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,10 @@
+2019-01-28  Jan Vrany  <jan.vrany@fit.cvut.cz>
+
+	* mi/mi-cmds.h (mi_cmd_complete): New function.
+	* mi/mi-main.c (mi_cmd_complete): Likewise.
+	* mi/mi-cmds.c: Define new MI command -complete.
+	* NEWS: Mention new -complete command.
+
 2019-01-24  Jan Vrany  <jan.vrany@fit.cvut.cz>
 
 	* completer.h (complete): New function.
diff --git a/gdb/NEWS b/gdb/NEWS
index eaef6aa384..3018313a46 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -96,6 +96,13 @@ maint show dwarf unwinders
 info proc files
   Display a list of open files for a process.
 
+* New MI commands
+
+-complete
+  This lists all the possible completions for the rest of the line, if it
+  were to be given as a command itself.  This is intended for use by MI frontends
+  in cases when separate CLI and MI channels cannot be used.
+
 * Changed commands
 
 Changes to the "frame", "select-frame", and "info frame" CLI commands.
diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog
index d38d4625c4..669f7e8ce6 100644
--- a/gdb/doc/ChangeLog
+++ b/gdb/doc/ChangeLog
@@ -1,3 +1,8 @@
+2019-01-28  Jan Vrany  <jan.vrany@fit.cvut.cz>
+
+	* gdb.texinfo (Miscellaneous GDB/MI Commands): Document new
+	MI command -complete.
+
 2019-01-16  Simon Marchi  <simon.marchi@ericsson.com>
 
 	* gdb.texinfo (GDB/MI Development and Front Ends): Add table of
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 173d18be6f..d7ef71367e 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -34317,6 +34317,37 @@ fullname="/home/nickrob/myprog.c",line="73",arch="i386:x86_64"@}
 (gdb)
 @end smallexample
 
+@subheading The @code{-complete} Command
+@findex -complete
+
+@subheading Synopsis
+
+@smallexample
+-complete @var{command}
+@end smallexample
+
+Show a list of completions for partially typed CLI @var{command}.
+
+This command is intended for @sc{gdb/mi} frontends that cannot use two separate
+CLI and MI channels - for example: because of lack of PTYs like on Windows or
+because @sc{gdb} is used remotely via a SSH connection.
+
+@subheading @value{GDBN} Command
+
+The corresponding @value{GDBN} command is @samp{complete}.
+
+@subheading Example
+
+@smallexample
+(gdb)
+-complete br
+^done,completions=["break","break-range"],max_completions_reached="0"
+(gdb)
+-complete "b madv"
+^done,completions=["b madvise"],max_completions_reached="0"
+(gdb)
+@end smallexample
+
 @node Annotations
 @chapter @value{GDBN} Annotations
 
diff --git a/gdb/mi/mi-cmds.c b/gdb/mi/mi-cmds.c
index bb7c20c777..683e990cdf 100644
--- a/gdb/mi/mi-cmds.c
+++ b/gdb/mi/mi-cmds.c
@@ -23,6 +23,7 @@
 #include "mi-cmds.h"
 #include "mi-main.h"
 
+
 struct mi_cmd;
 static struct mi_cmd **lookup_table (const char *command);
 static void build_table (struct mi_cmd *commands);
@@ -75,6 +76,7 @@ static struct mi_cmd mi_cmds[] =
                    &mi_suppress_notification.breakpoint),
   DEF_MI_CMD_MI_1 ("catch-unload", mi_cmd_catch_unload,
                    &mi_suppress_notification.breakpoint),
+  DEF_MI_CMD_MI ("complete", mi_cmd_complete),
   DEF_MI_CMD_MI ("data-disassemble", mi_cmd_disassemble),
   DEF_MI_CMD_MI ("data-evaluate-expression", mi_cmd_data_evaluate_expression),
   DEF_MI_CMD_MI ("data-list-changed-registers",
diff --git a/gdb/mi/mi-cmds.h b/gdb/mi/mi-cmds.h
index af38363cfc..32bd747d43 100644
--- a/gdb/mi/mi-cmds.h
+++ b/gdb/mi/mi-cmds.h
@@ -125,6 +125,7 @@ extern mi_cmd_argv_ftype mi_cmd_var_update;
 extern mi_cmd_argv_ftype mi_cmd_enable_pretty_printing;
 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.  */
 
diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c
index 7176963845..b2ff8df8de 100644
--- a/gdb/mi/mi-main.c
+++ b/gdb/mi/mi-main.c
@@ -2709,6 +2709,50 @@ mi_cmd_trace_frame_collected (const char *command, char **argv, int argc)
   }
 }
 
+/* Implement the "-complete" command.  */
+void
+mi_cmd_complete (const char *command, char **argv, int argc)
+{
+  if (argc != 1)
+    {
+      error (_("Usage: -complete COMMAND"));
+    }
+  if (max_completions == 0)
+    {
+      error (_("max-completions is zero,"
+               " completion is disabled.\n"));
+    }
+
+  int quote_char = '\0';
+  const char *word;
+
+  completion_result result = complete (argv[0], &word, &quote_char);
+
+  std::string arg_prefix (argv[0], word - argv[0]);
+
+  struct ui_out *uiout = current_uiout;
+  {
+    ui_out_emit_list completions_emitter (uiout, "completions");
+
+    if (result.number_matches == 1)
+      {
+        uiout->field_fmt(NULL, "%s%s", arg_prefix.c_str (), result.match_list[0]);
+      }
+    else
+      {
+        result.sort_match_list ();
+        for (size_t i = 0; i < result.number_matches; i++)
+          {
+            uiout->field_fmt(NULL, "%s%s", arg_prefix.c_str (),
+                                           result.match_list[i + 1]);
+          }
+      }
+  }
+  uiout->field_string ("max_completions_reached",
+                       result.number_matches == max_completions ? "1" : "0");
+}
+
+
 void
 _initialize_mi_main (void)
 {
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index 7d8c7908fe..f395de64a8 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,3 +1,7 @@
+2019-01-28  Jan Vrany  <jan.vrany@fit.cvut.cz>
+
+	* gdb.mi/mi-complete.exp: New file.
+
 2019-01-21  Alan Hayward  <alan.hayward@arm.com>
 	* gdb.base/infcall-nested-structs.exp: Test C++ in addition to C.
 
diff --git a/gdb/testsuite/gdb.mi/mi-complete.exp b/gdb/testsuite/gdb.mi/mi-complete.exp
new file mode 100644
index 0000000000..2963bd2ac5
--- /dev/null
+++ b/gdb/testsuite/gdb.mi/mi-complete.exp
@@ -0,0 +1,71 @@
+# Copyright 2018 Free Software Foundation, Inc.
+
+# 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/>.
+
+# Verify -data-evaluate-expression. There are really minimal tests.
+
+# The goal is not to test gdb functionality, which is done by other tests,
+# but to verify the correct output response to MI operations.
+#
+
+load_lib mi-support.exp
+set MIFLAGS "-i=mi"
+
+gdb_exit
+if [mi_gdb_start] {
+    continue
+}
+
+standard_testfile basics.c
+
+if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+     untested "failed to compile"
+     return -1
+}
+
+mi_run_to_main
+
+mi_gdb_test "1-complete br" \
+            "1\\^done,completions=\\\[.*\"break\",.*\"break-range\".*\\\],max_completions_reached=\"0\"" \
+            "-complete br"
+
+# Check empty completion list
+mi_gdb_test "5-complete bogus" \
+            "5\\^done,completions=\\\[\\\],max_completions_reached=\"0\"" \
+            "-complete bogus"
+
+
+# Check completions for commands with space
+mi_gdb_test "4-complete \"b mai\"" \
+            "4\\^done,completions=\\\[.*\"b main\".*\\\],max_completions_reached=\"0\"" \
+            "-complete \"b mai\""
+
+mi_gdb_test "-info-gdb-mi-command complete" \
+            "\\^done,command=\{exists=\"true\"\}" \
+            "-info-gdb-mi-command complete"
+
+# Limit max completions and check that max_completions_reached=\"0\" is set
+# to 1.
+send_gdb "set max-completions 1\n"
+
+mi_gdb_test "2-complete br" \
+            ".*2\\^done,completions=\\\[\"br\[A-Za-z0-9-\]+\"\\\],max_completions_reached=\"1\"" \
+            "-complete br (max-completions 1)"
+
+# Disable completions and check an error is returned
+send_gdb "set max-completions 0\n"
+
+mi_gdb_test "3-complete br" \
+            ".*3\\^error,msg=\".*" \
+            "-complete br (max-completions 0)"
-- 
2.20.1

^ permalink raw reply	[flat|nested] 50+ messages in thread

* [PATCH v2 1/2] MI: extract command completion logic from complete_command()
  2019-01-28 12:41   ` [PATCH v2 0/2] " Jan Vrany
  2019-01-28 12:41     ` [PATCH v2 2/2] " Jan Vrany
@ 2019-01-28 12:41     ` Jan Vrany
  2019-02-27 20:41       ` Pedro Alves
       [not found]     ` <9ddd13d90ac5d77067f5690743149be8a2dcdd1a.camel@fit.cvut.cz>
                       ` (13 subsequent siblings)
  15 siblings, 1 reply; 50+ messages in thread
From: Jan Vrany @ 2019-01-28 12:41 UTC (permalink / raw)
  To: gdb-patches; +Cc: Jan Vrany

Extract completion logic from CLI complete_command() into a new
helper function complete().

gdb/Changelog:

	* completer.h (complete): New function.
	* completer.c (complete): Likewise.
	* cli/cli-cmds.c: (complete_command): Update to use new complete()
	function defined in completer.h
---
 gdb/ChangeLog      |  7 +++++++
 gdb/cli/cli-cmds.c | 32 ++------------------------------
 gdb/completer.c    | 34 ++++++++++++++++++++++++++++++++++
 gdb/completer.h    |  8 ++++++++
 4 files changed, 51 insertions(+), 30 deletions(-)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 8e03dbf883..17e29a7807 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,10 @@
+2019-01-24  Jan Vrany  <jan.vrany@fit.cvut.cz>
+
+	* completer.h (complete): New function.
+	* completer.c (complete): Likewise.
+	* cli/cli-cmds.c: (complete_command): Update to use new complete() 
+	function defined in completer.h
+
 2019-01-22  Philippe Waroquiers  <philippe.waroquiers@skynet.be>
 
 	* event-top.c (handle_line_of_input): use unique_xmalloc_ptr for
diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index 57cfad441c..f2c2311e68 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -243,41 +243,13 @@ complete_command (const char *arg, int from_tty)
   if (arg == NULL)
     arg = "";
 
-  completion_tracker tracker_handle_brkchars;
-  completion_tracker tracker_handle_completions;
-  completion_tracker *tracker;
-
   int quote_char = '\0';
   const char *word;
-
-  TRY
-    {
-      word = completion_find_completion_word (tracker_handle_brkchars,
-					      arg, &quote_char);
-
-      /* Completers that provide a custom word point in the
-	 handle_brkchars phase also compute their completions then.
-	 Completers that leave the completion word handling to readline
-	 must be called twice.  */
-      if (tracker_handle_brkchars.use_custom_word_point ())
-	tracker = &tracker_handle_brkchars;
-      else
-	{
-	  complete_line (tracker_handle_completions, word, arg, strlen (arg));
-	  tracker = &tracker_handle_completions;
-	}
-    }
-  CATCH (ex, RETURN_MASK_ALL)
-    {
-      return;
-    }
-  END_CATCH
+  
+  completion_result result = complete (arg, &word, &quote_char);
 
   std::string arg_prefix (arg, word - arg);
 
-  completion_result result
-    = tracker->build_completion_result (word, word - arg, strlen (arg));
-
   if (result.number_matches != 0)
     {
       if (result.number_matches == 1)
diff --git a/gdb/completer.c b/gdb/completer.c
index fed815a53c..2f94932184 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -1615,6 +1615,40 @@ make_completion_match_str (gdb::unique_xmalloc_ptr<char> &&match_name,
   return gdb::unique_xmalloc_ptr<char> (newobj);
 }
 
+completion_result
+complete (const char *line, char const **word, int *quote_char)
+{
+  completion_tracker tracker_handle_brkchars;
+  completion_tracker tracker_handle_completions;
+  completion_tracker *tracker;
+  
+  TRY
+    {
+      *word = completion_find_completion_word (tracker_handle_brkchars,
+					      line, quote_char);
+
+      /* Completers that provide a custom word point in the
+	 handle_brkchars phase also compute their completions then.
+	 Completers that leave the completion word handling to readline
+	 must be called twice.  */
+      if (tracker_handle_brkchars.use_custom_word_point ())
+	tracker = &tracker_handle_brkchars;
+      else
+	{
+	  complete_line (tracker_handle_completions, *word, line, strlen (line));
+	  tracker = &tracker_handle_completions;
+	}
+    }
+  CATCH (ex, RETURN_MASK_ALL)
+    {
+      return {};
+    }
+  END_CATCH
+
+  return tracker->build_completion_result (*word, *word - line, strlen (line));  
+}
+
+
 /* Generate completions all at once.  Does nothing if max_completions
    is 0.  If max_completions is non-negative, this will collect at
    most max_completions strings.
diff --git a/gdb/completer.h b/gdb/completer.h
index e81243a721..bfbead235b 100644
--- a/gdb/completer.h
+++ b/gdb/completer.h
@@ -510,6 +510,14 @@ extern void complete_line (completion_tracker &tracker,
 			   const char *line_buffer,
 			   int point);
 
+/* Complete LINE and return completion results. For completion purposes,
+   cursor position is assumed to be at the end of LINE. WORD is set to 
+   the end of word to complete. QUOTE_CHAR is set to the opening quote 
+   character if we found an unclosed quoted substring, '\0' otherwise.
+ */
+extern completion_result
+  complete (const char *line, char const **word, int *quote_char);
+
 /* Find the bounds of the word in TEXT for completion purposes, and
    return a pointer to the end of the word.  Calls the completion
    machinery for a handle_brkchars phase (using TRACKER) to figure out
-- 
2.20.1

^ permalink raw reply	[flat|nested] 50+ messages in thread

* [PATCH v2 0/2] MI: Add new command -complete
  2019-01-17 20:29 ` Tom Tromey
  2019-01-17 21:01   ` Jan Vrany
@ 2019-01-28 12:41   ` Jan Vrany
  2019-01-28 12:41     ` [PATCH v2 2/2] " Jan Vrany
                       ` (15 more replies)
  1 sibling, 16 replies; 50+ messages in thread
From: Jan Vrany @ 2019-01-28 12:41 UTC (permalink / raw)
  To: gdb-patches; +Cc: Jan Vrany

This is a rework of previous patch based on Tom's comments.

Another thing worth considering (not done in this version) is to
have more structured result. Instead of (current version):

-complete "br m"
=^done,completions=["br main", "br madvise"]

respond with something like (proposed change):

-complete "br m"
=^done,text="br m",common="a",matches=["in", "dvise"]

The rarionale is that frontend most likely needs these three
values anyway to implement completion. It can, indeed compute
them from full list as returned now, but GDB has these values
already so it would save the frontend doing the same work again. 

OTOH, current output is more on par with CLI command output. 

What do you think? 

Differences v1 -> v2: 

  * extracted common completion logic to a new helper function
  * implemented MI command using a new mi-specific function rather
    than using CLI implementation.


Jan Vrany (2):
  MI: extract command completion logic from complete_command()
  MI: Add new command -complete

 gdb/ChangeLog                        | 14 ++++++
 gdb/NEWS                             |  7 +++
 gdb/cli/cli-cmds.c                   | 32 +------------
 gdb/completer.c                      | 34 +++++++++++++
 gdb/completer.h                      |  8 ++++
 gdb/doc/ChangeLog                    |  5 ++
 gdb/doc/gdb.texinfo                  | 31 ++++++++++++
 gdb/mi/mi-cmds.c                     |  2 +
 gdb/mi/mi-cmds.h                     |  1 +
 gdb/mi/mi-main.c                     | 44 +++++++++++++++++
 gdb/testsuite/ChangeLog              |  4 ++
 gdb/testsuite/gdb.mi/mi-complete.exp | 71 ++++++++++++++++++++++++++++
 12 files changed, 223 insertions(+), 30 deletions(-)
 create mode 100644 gdb/testsuite/gdb.mi/mi-complete.exp

-- 
2.20.1

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH v2 0/2] MI: Add new command -complete
       [not found]     ` <9ddd13d90ac5d77067f5690743149be8a2dcdd1a.camel@fit.cvut.cz>
@ 2019-02-13  9:24       ` Jan Vrany
  2019-02-19  7:33         ` Jan Vrany
  0 siblings, 1 reply; 50+ messages in thread
From: Jan Vrany @ 2019-02-13  9:24 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey

Polite ping. 

Jan

On Mon, 2019-02-04 at 10:00 +0000, Jan Vrany wrote:
> Polite ping. 
> 
> Jan
> 
> On Mon, 2019-01-28 at 12:40 +0000, Jan Vrany wrote:
> > This is a rework of previous patch based on Tom's comments.
> > 
> > Another thing worth considering (not done in this version) is to
> > have more structured result. Instead of (current version):
> > 
> > -complete "br m"
> > =^done,completions=["br main", "br madvise"]
> > 
> > respond with something like (proposed change):
> > 
> > -complete "br m"
> > =^done,text="br m",common="a",matches=["in", "dvise"]
> > 
> > The rarionale is that frontend most likely needs these three
> > values anyway to implement completion. It can, indeed compute
> > them from full list as returned now, but GDB has these values
> > already so it would save the frontend doing the same work again. 
> > 
> > OTOH, current output is more on par with CLI command output. 
> > 
> > What do you think? 
> > 
> > Differences v1 -> v2: 
> > 
> >   * extracted common completion logic to a new helper function
> >   * implemented MI command using a new mi-specific function rather
> >     than using CLI implementation.
> > 
> > 
> > Jan Vrany (2):
> >   MI: extract command completion logic from complete_command()
> >   MI: Add new command -complete
> > 
> >  gdb/ChangeLog                        | 14 ++++++
> >  gdb/NEWS                             |  7 +++
> >  gdb/cli/cli-cmds.c                   | 32 +------------
> >  gdb/completer.c                      | 34 +++++++++++++
> >  gdb/completer.h                      |  8 ++++
> >  gdb/doc/ChangeLog                    |  5 ++
> >  gdb/doc/gdb.texinfo                  | 31 ++++++++++++
> >  gdb/mi/mi-cmds.c                     |  2 +
> >  gdb/mi/mi-cmds.h                     |  1 +
> >  gdb/mi/mi-main.c                     | 44 +++++++++++++++++
> >  gdb/testsuite/ChangeLog              |  4 ++
> >  gdb/testsuite/gdb.mi/mi-complete.exp | 71 ++++++++++++++++++++++++++++
> >  12 files changed, 223 insertions(+), 30 deletions(-)
> >  create mode 100644 gdb/testsuite/gdb.mi/mi-complete.exp
> > 

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH v2 0/2] MI: Add new command -complete
  2019-02-13  9:24       ` [PATCH v2 0/2] MI: Add new command -complete Jan Vrany
@ 2019-02-19  7:33         ` Jan Vrany
  0 siblings, 0 replies; 50+ messages in thread
From: Jan Vrany @ 2019-02-19  7:33 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey

Polite ping. 

Jan

On Wed, 2019-02-13 at 09:24 +0000, Jan Vrany wrote:
> Polite ping. 
> 
> Jan
> 
> On Mon, 2019-02-04 at 10:00 +0000, Jan Vrany wrote:
> > Polite ping. 
> > 
> > Jan
> > 
> > On Mon, 2019-01-28 at 12:40 +0000, Jan Vrany wrote:
> > > This is a rework of previous patch based on Tom's comments.
> > > 
> > > Another thing worth considering (not done in this version) is to
> > > have more structured result. Instead of (current version):
> > > 
> > > -complete "br m"
> > > =^done,completions=["br main", "br madvise"]
> > > 
> > > respond with something like (proposed change):
> > > 
> > > -complete "br m"
> > > =^done,text="br m",common="a",matches=["in", "dvise"]
> > > 
> > > The rarionale is that frontend most likely needs these three
> > > values anyway to implement completion. It can, indeed compute
> > > them from full list as returned now, but GDB has these values
> > > already so it would save the frontend doing the same work again. 
> > > 
> > > OTOH, current output is more on par with CLI command output. 
> > > 
> > > What do you think? 
> > > 
> > > Differences v1 -> v2: 
> > > 
> > >   * extracted common completion logic to a new helper function
> > >   * implemented MI command using a new mi-specific function rather
> > >     than using CLI implementation.
> > > 
> > > 
> > > Jan Vrany (2):
> > >   MI: extract command completion logic from complete_command()
> > >   MI: Add new command -complete
> > > 
> > >  gdb/ChangeLog                        | 14 ++++++
> > >  gdb/NEWS                             |  7 +++
> > >  gdb/cli/cli-cmds.c                   | 32 +------------
> > >  gdb/completer.c                      | 34 +++++++++++++
> > >  gdb/completer.h                      |  8 ++++
> > >  gdb/doc/ChangeLog                    |  5 ++
> > >  gdb/doc/gdb.texinfo                  | 31 ++++++++++++
> > >  gdb/mi/mi-cmds.c                     |  2 +
> > >  gdb/mi/mi-cmds.h                     |  1 +
> > >  gdb/mi/mi-main.c                     | 44 +++++++++++++++++
> > >  gdb/testsuite/ChangeLog              |  4 ++
> > >  gdb/testsuite/gdb.mi/mi-complete.exp | 71 ++++++++++++++++++++++++++++
> > >  12 files changed, 223 insertions(+), 30 deletions(-)
> > >  create mode 100644 gdb/testsuite/gdb.mi/mi-complete.exp
> > > 

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH v2 0/2] MI: Add new command -complete
  2019-01-28 12:41   ` [PATCH v2 0/2] " Jan Vrany
                       ` (2 preceding siblings ...)
       [not found]     ` <9ddd13d90ac5d77067f5690743149be8a2dcdd1a.camel@fit.cvut.cz>
@ 2019-02-20 21:20     ` Tom Tromey
  2019-02-21 16:05       ` Jan Vrany
  2019-03-04 14:52     ` [PATCH v3 2/2] " Jan Vrany
                       ` (11 subsequent siblings)
  15 siblings, 1 reply; 50+ messages in thread
From: Tom Tromey @ 2019-02-20 21:20 UTC (permalink / raw)
  To: Jan Vrany; +Cc: gdb-patches

>>>>> "Jan" == Jan Vrany <jan.vrany@fit.cvut.cz> writes:

Jan> respond with something like (proposed change):

Jan> -complete "br m"
Jan> =^done,text="br m",common="a",matches=["in", "dvise"]

Jan> The rarionale is that frontend most likely needs these three
Jan> values anyway to implement completion. It can, indeed compute
Jan> them from full list as returned now, but GDB has these values
Jan> already so it would save the frontend doing the same work again. 

Jan> OTOH, current output is more on par with CLI command output. 

Jan> What do you think? 

I think if this form seems more useful to MI users, then it's fine to
implement it.  I would say this is more important than making it similar
to the CLI.

Tom

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH v2 0/2] MI: Add new command -complete
  2019-02-20 21:20     ` Tom Tromey
@ 2019-02-21 16:05       ` Jan Vrany
  2019-02-26 19:49         ` Tom Tromey
  0 siblings, 1 reply; 50+ messages in thread
From: Jan Vrany @ 2019-02-21 16:05 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches, gdb

On Wed, 2019-02-20 at 14:20 -0700, Tom Tromey wrote:
> > > > > > "Jan" == Jan Vrany <jan.vrany@fit.cvut.cz> writes:
> 
> Jan> respond with something like (proposed change):
> 
> Jan> -complete "br m"
> Jan> =^done,text="br m",common="a",matches=["in", "dvise"]
> 
> Jan> The rarionale is that frontend most likely needs these three
> Jan> values anyway to implement completion. It can, indeed compute
> Jan> them from full list as returned now, but GDB has these values
> Jan> already so it would save the frontend doing the same work again. 
> 
> Jan> OTOH, current output is more on par with CLI command output. 
> 
> Jan> What do you think? 
> 
> I think if this form seems more useful to MI users, then it's fine to
> implement it.  I would say this is more important than making it similar
> to the CLI.

Are there any other GDB/MI users to comment on this? What would you prefer?

Jan

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH v2 0/2] MI: Add new command -complete
  2019-02-21 16:05       ` Jan Vrany
@ 2019-02-26 19:49         ` Tom Tromey
  2019-02-27 10:41           ` Jan Vrany
  2019-02-27 20:41           ` Pedro Alves
  0 siblings, 2 replies; 50+ messages in thread
From: Tom Tromey @ 2019-02-26 19:49 UTC (permalink / raw)
  To: Jan Vrany; +Cc: Tom Tromey, gdb-patches, gdb

>>>>> "Jan" == Jan Vrany <jan.vrany@fit.cvut.cz> writes:

Jan> Are there any other GDB/MI users to comment on this? What would you
Jan> prefer?

Given the lack of response, I think you should just say which you
prefer.  If you think it would be better the "other" way, go for it.
Or if you'd rather the patches you already have, let me know.

thanks,
Tom

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH v2 0/2] MI: Add new command -complete
  2019-02-26 19:49         ` Tom Tromey
@ 2019-02-27 10:41           ` Jan Vrany
  2019-02-27 20:41           ` Pedro Alves
  1 sibling, 0 replies; 50+ messages in thread
From: Jan Vrany @ 2019-02-27 10:41 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches, gdb

On Tue, 2019-02-26 at 12:49 -0700, Tom Tromey wrote:
> > > > > > "Jan" == Jan Vrany <jan.vrany@fit.cvut.cz> writes:
> 
> Or if you'd rather the patches you already have, let me know.
> 

Yes, I would prefer to have it as implemented in v2 patch mini-series
Thanks!

Jan

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH v2 1/2] MI: extract command completion logic from complete_command()
  2019-01-28 12:41     ` [PATCH v2 1/2] MI: extract command completion logic from complete_command() Jan Vrany
@ 2019-02-27 20:41       ` Pedro Alves
  0 siblings, 0 replies; 50+ messages in thread
From: Pedro Alves @ 2019-02-27 20:41 UTC (permalink / raw)
  To: Jan Vrany, gdb-patches

Hi Jan,

Some formatting nits below.

On 01/28/2019 12:41 PM, Jan Vrany wrote:
> Extract completion logic from CLI complete_command() into a new
> helper function complete().
> 
> gdb/Changelog:
> 
> 	* completer.h (complete): New function.
> 	* completer.c (complete): Likewise.
> 	* cli/cli-cmds.c: (complete_command): Update to use new complete()
> 	function defined in completer.h

Missing period after "in completer.h".

> ---
>  gdb/ChangeLog      |  7 +++++++
>  gdb/cli/cli-cmds.c | 32 ++------------------------------
>  gdb/completer.c    | 34 ++++++++++++++++++++++++++++++++++
>  gdb/completer.h    |  8 ++++++++
>  4 files changed, 51 insertions(+), 30 deletions(-)
> 
> diff --git a/gdb/ChangeLog b/gdb/ChangeLog
> index 8e03dbf883..17e29a7807 100644
> --- a/gdb/ChangeLog
> +++ b/gdb/ChangeLog
> @@ -1,3 +1,10 @@
> +2019-01-24  Jan Vrany  <jan.vrany@fit.cvut.cz>
> +
> +	* completer.h (complete): New function.
> +	* completer.c (complete): Likewise.
> +	* cli/cli-cmds.c: (complete_command): Update to use new complete() 
> +	function defined in completer.h
> +
>  2019-01-22  Philippe Waroquiers  <philippe.waroquiers@skynet.be>
>  
>  	* event-top.c (handle_line_of_input): use unique_xmalloc_ptr for
> diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
> index 57cfad441c..f2c2311e68 100644
> --- a/gdb/cli/cli-cmds.c
> +++ b/gdb/cli/cli-cmds.c
> @@ -243,41 +243,13 @@ complete_command (const char *arg, int from_tty)
>    if (arg == NULL)
>      arg = "";
>  
> -  completion_tracker tracker_handle_brkchars;
> -  completion_tracker tracker_handle_completions;
> -  completion_tracker *tracker;
> -
>    int quote_char = '\0';
>    const char *word;
> -
> -  TRY
> -    {
> -      word = completion_find_completion_word (tracker_handle_brkchars,
> -					      arg, &quote_char);
> -
> -      /* Completers that provide a custom word point in the
> -	 handle_brkchars phase also compute their completions then.
> -	 Completers that leave the completion word handling to readline
> -	 must be called twice.  */
> -      if (tracker_handle_brkchars.use_custom_word_point ())
> -	tracker = &tracker_handle_brkchars;
> -      else
> -	{
> -	  complete_line (tracker_handle_completions, word, arg, strlen (arg));
> -	  tracker = &tracker_handle_completions;
> -	}
> -    }
> -  CATCH (ex, RETURN_MASK_ALL)
> -    {
> -      return;
> -    }
> -  END_CATCH
> +  
> +  completion_result result = complete (arg, &word, &quote_char);
>  
>    std::string arg_prefix (arg, word - arg);
>  
> -  completion_result result
> -    = tracker->build_completion_result (word, word - arg, strlen (arg));
> -
>    if (result.number_matches != 0)
>      {
>        if (result.number_matches == 1)
> diff --git a/gdb/completer.c b/gdb/completer.c
> index fed815a53c..2f94932184 100644
> --- a/gdb/completer.c
> +++ b/gdb/completer.c
> @@ -1615,6 +1615,40 @@ make_completion_match_str (gdb::unique_xmalloc_ptr<char> &&match_name,
>    return gdb::unique_xmalloc_ptr<char> (newobj);
>  }
>  
> +completion_result
> +complete (const char *line, char const **word, int *quote_char)

Add:

/* See complete.h.  */

Make sure to leave an empty line between comment and function.


> +{
> +  completion_tracker tracker_handle_brkchars;
> +  completion_tracker tracker_handle_completions;
> +  completion_tracker *tracker;
> +  
> +  TRY
> +    {
> +      *word = completion_find_completion_word (tracker_handle_brkchars,
> +					      line, quote_char);
> +
> +      /* Completers that provide a custom word point in the
> +	 handle_brkchars phase also compute their completions then.
> +	 Completers that leave the completion word handling to readline
> +	 must be called twice.  */
> +      if (tracker_handle_brkchars.use_custom_word_point ())
> +	tracker = &tracker_handle_brkchars;
> +      else
> +	{
> +	  complete_line (tracker_handle_completions, *word, line, strlen (line));
> +	  tracker = &tracker_handle_completions;
> +	}
> +    }
> +  CATCH (ex, RETURN_MASK_ALL)
> +    {
> +      return {};
> +    }
> +  END_CATCH
> +
> +  return tracker->build_completion_result (*word, *word - line, strlen (line));  
> +}
> +
> +
>  /* Generate completions all at once.  Does nothing if max_completions
>     is 0.  If max_completions is non-negative, this will collect at
>     most max_completions strings.
> diff --git a/gdb/completer.h b/gdb/completer.h
> index e81243a721..bfbead235b 100644
> --- a/gdb/completer.h
> +++ b/gdb/completer.h
> @@ -510,6 +510,14 @@ extern void complete_line (completion_tracker &tracker,
>  			   const char *line_buffer,
>  			   int point);
>  
> +/* Complete LINE and return completion results. For completion purposes,
> +   cursor position is assumed to be at the end of LINE. WORD is set to 
> +   the end of word to complete. QUOTE_CHAR is set to the opening quote 
> +   character if we found an unclosed quoted substring, '\0' otherwise.
> + */

Double space after periods ending sentences.  '*/' goes at the previous line.

Like this:

/* Complete LINE and return completion results.  For completion
   purposes, cursor position is assumed to be at the end of LINE.
   WORD is set to the end of word to complete.  QUOTE_CHAR is set to
   the opening quote character if we found an unclosed quoted
   substring, '\0' otherwise.  */

> +extern completion_result
> +  complete (const char *line, char const **word, int *quote_char);
> +
>  /* Find the bounds of the word in TEXT for completion purposes, and
>     return a pointer to the end of the word.  Calls the completion
>     machinery for a handle_brkchars phase (using TRACKER) to figure out
> 
Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH v2 0/2] MI: Add new command -complete
  2019-02-26 19:49         ` Tom Tromey
  2019-02-27 10:41           ` Jan Vrany
@ 2019-02-27 20:41           ` Pedro Alves
  2019-02-28 10:18             ` Jan Vrany
  1 sibling, 1 reply; 50+ messages in thread
From: Pedro Alves @ 2019-02-27 20:41 UTC (permalink / raw)
  To: Tom Tromey, Jan Vrany; +Cc: gdb-patches, gdb

I'm totally not against this new command at all, but I have to say that I'd be
much more thrilled if someone just spent the time to make separate CLI/MI
channels work on Windows too.  The channel doesn't _have_ to be a PTY.

On 02/26/2019 07:49 PM, Tom Tromey wrote:
>>>>>> "Jan" == Jan Vrany <jan.vrany@fit.cvut.cz> writes:
> 
> Jan> Are there any other GDB/MI users to comment on this? What would you
> Jan> prefer?
> 
> Given the lack of response, I think you should just say which you
> prefer.  If you think it would be better the "other" way, go for it.
> Or if you'd rather the patches you already have, let me know.

Jan, please consider the wildmatching case.  E.g., when debugging GDB itself:

(gdb) b push_bac<TAB>
Display all 102 possibilities? (y or n)
debug_names::offset_vec_tmpl<unsigned int>::push_back_reorder(unsigned long)
debug_names::offset_vec_tmpl<unsigned long>::push_back_reorder(unsigned long)
std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::push_back(char)
...

The frontend needs to complete "b push_bac" -> "b push_back", and present
the matches.

But the least common denominator is not at the start of the matches
strings.  How will a frontend compute the LCD from the matches list alone?

Please mind the "2018" copyright year in the testcase.

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH v2 0/2] MI: Add new command -complete
  2019-02-27 20:41           ` Pedro Alves
@ 2019-02-28 10:18             ` Jan Vrany
  2019-03-05 20:53               ` Pedro Alves
  0 siblings, 1 reply; 50+ messages in thread
From: Jan Vrany @ 2019-02-28 10:18 UTC (permalink / raw)
  To: Pedro Alves, Tom Tromey; +Cc: gdb-patches, gdb

Hello,

On Wed, 2019-02-27 at 20:41 +0000, Pedro Alves wrote:
> I'm totally not against this new command at all, but I have to say that I'd be
> much more thrilled if someone just spent the time to make separate CLI/MI
> channels work on Windows too.  The channel doesn't _have_ to be a PTY.

I know. That was my initial idea just to use named pipes year or two back
when I started to support Windows. However:

1) Completion CLI is AFAIK implemented using readline which has problems 
   working over pipes. Actually, I never got CLI working satisfactorily
   on Windows even when I just run GDB "normally" from Windows command shell (`cmd.exe`),
   let alone over pipes or alike. 

2) Even if it would work, there are still other usecases which working readline
   and separate CLI/MI channel on Windows would not support. For example:
   
   (a) at the moment we use "my" frontend [1], [2] running on developers laptop 
       connecting over ssh to a GDB that runs on remote RISC-V machine. Essentially,
       instead of lauching gdb locally (on dev's machine) like: 

         gdb path/to/binary

       we do 

         ssh unleashed gdb -i=mi2 path/to/binary

       In this case, opening a secondary MI channel is very problematic. 
       -complete command gives me at least a way to implement completion which
       we found very important. There are still some quirks with that, sure, 
       like when you run `pi` or `shell` commands it completely ruins the MI 
       channel (this is a bug have not yet looked at let alone fixed but it is 
       on my very long list).

   (b) another frontend feature asked was to provide a kind of "workspace" for GDB commands,
       This is essentially a text editor in which you write ad-hoc commands (possibly
       with comments) and execute them by selecting portion of a text and pressing a button
       (or with a shortcut). -complete command would allow me to implement completion
       in such "workspace" 

       (I know, this may sound like a weird UI, but this is essentially what a Smalltalk workspace
       is but for GDB commands. So far users of this frontend are mainly - me including -  
       a small bunch of old smalltalkers working on virtual machines)

[1]: https://bitbucket.org/janvrany/jv-vdb/src/default/
[2]: https://bitbucket.org/janvrany/jv-libgdbs/src/default/
[3]: https://bitbucket.org/janvrany/jv-vdb/src/default/doc/Invoking.md
   
> 
> On 02/26/2019 07:49 PM, Tom Tromey wrote:
> > > > > > > "Jan" == Jan Vrany <jan.vrany@fit.cvut.cz> writes:
> > 
> > Jan> Are there any other GDB/MI users to comment on this? What would you
> > Jan> prefer?
> > 
> > Given the lack of response, I think you should just say which you
> > prefer.  If you think it would be better the "other" way, go for it.
> > Or if you'd rather the patches you already have, let me know.
> 
> Jan, please consider the wildmatching case.  E.g., when debugging GDB itself:
> 
> (gdb) b push_bac<TAB>
> Display all 102 possibilities? (y or n)
> debug_names::offset_vec_tmpl<unsigned int>::push_back_reorder(unsigned long)
> debug_names::offset_vec_tmpl<unsigned long>::push_back_reorder(unsigned long)
> std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::push_back(char)
> ...
> 
> The frontend needs to complete "b push_bac" -> "b push_back", and present
> the matches.
> 
> But the least common denominator is not at the start of the matches
> strings.  How will a frontend compute the LCD from the matches list alone?

I see. I was not aware of this behavior, thanks for pointing this out! I'll address that
in next iterations. 

Thanks a lot! 

Jan

^ permalink raw reply	[flat|nested] 50+ messages in thread

* [PATCH v3 2/2] MI: Add new command -complete
  2019-01-28 12:41   ` [PATCH v2 0/2] " Jan Vrany
                       ` (3 preceding siblings ...)
  2019-02-20 21:20     ` Tom Tromey
@ 2019-03-04 14:52     ` Jan Vrany
  2019-03-04 17:35       ` Eli Zaretskii
  2019-04-03 19:23       ` Pedro Alves
  2019-03-04 14:52     ` [PATCH v3 1/2] MI: extract command completion logic from complete_command() Jan Vrany
                       ` (10 subsequent siblings)
  15 siblings, 2 replies; 50+ messages in thread
From: Jan Vrany @ 2019-03-04 14:52 UTC (permalink / raw)
  To: gdb-patches; +Cc: Jan Vrany

There is a CLI command 'complete' intended to use with emacs. Such a command
would also be useful for MI frontends, when separate CLI and MI channels cannot
be used. For example, on Windows (because of lack of PTYs) or when GDB is used
through SSH session.

This commit adds a new '-complete' MI command.

gdb/Changelog:
2019-01-28  Jan Vrany  <jan.vrany@fit.cvut.cz>

	* mi/mi-cmds.h (mi_cmd_complete): New function.
	* mi/mi-main.c (mi_cmd_complete): Likewise.
	* mi/mi-cmds.c: Define new MI command -complete.
	* NEWS: Mention new -complete command.

gdb/doc/ChangeLog:
2019-01-28  Jan Vrany  <jan.vrany@fit.cvut.cz>

	* gdb.texinfo (Miscellaneous GDB/MI Commands): Document new
	MI command -complete.

gdb/testsuite/ChangeLog:
2019-01-28  Jan Vrany  <jan.vrany@fit.cvut.cz>

	* gdb.mi/mi-complete.exp: New file.
	* gdb.mi/mi-complete.cc: Likewise.
---
 gdb/ChangeLog                        |  7 +++
 gdb/NEWS                             |  7 +++
 gdb/doc/ChangeLog                    |  5 ++
 gdb/doc/gdb.texinfo                  | 57 +++++++++++++++++++++
 gdb/mi/mi-cmds.c                     |  2 +
 gdb/mi/mi-cmds.h                     |  1 +
 gdb/mi/mi-main.c                     | 49 ++++++++++++++++++
 gdb/testsuite/ChangeLog              |  5 ++
 gdb/testsuite/gdb.mi/mi-complete.cc  | 21 ++++++++
 gdb/testsuite/gdb.mi/mi-complete.exp | 75 ++++++++++++++++++++++++++++
 10 files changed, 229 insertions(+)
 create mode 100644 gdb/testsuite/gdb.mi/mi-complete.cc
 create mode 100644 gdb/testsuite/gdb.mi/mi-complete.exp

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index be65f4a2d6..9865bbbf4d 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,10 @@
+2019-01-28  Jan Vrany  <jan.vrany@fit.cvut.cz>
+
+	* mi/mi-cmds.h (mi_cmd_complete): New function.
+	* mi/mi-main.c (mi_cmd_complete): Likewise.
+	* mi/mi-cmds.c: Define new MI command -complete.
+	* NEWS: Mention new -complete command.
+
 2019-01-24  Jan Vrany  <jan.vrany@fit.cvut.cz>
 
 	* completer.h (complete): New function.
diff --git a/gdb/NEWS b/gdb/NEWS
index eaef6aa384..3018313a46 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -96,6 +96,13 @@ maint show dwarf unwinders
 info proc files
   Display a list of open files for a process.
 
+* New MI commands
+
+-complete
+  This lists all the possible completions for the rest of the line, if it
+  were to be given as a command itself.  This is intended for use by MI frontends
+  in cases when separate CLI and MI channels cannot be used.
+
 * Changed commands
 
 Changes to the "frame", "select-frame", and "info frame" CLI commands.
diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog
index d38d4625c4..669f7e8ce6 100644
--- a/gdb/doc/ChangeLog
+++ b/gdb/doc/ChangeLog
@@ -1,3 +1,8 @@
+2019-01-28  Jan Vrany  <jan.vrany@fit.cvut.cz>
+
+	* gdb.texinfo (Miscellaneous GDB/MI Commands): Document new
+	MI command -complete.
+
 2019-01-16  Simon Marchi  <simon.marchi@ericsson.com>
 
 	* gdb.texinfo (GDB/MI Development and Front Ends): Add table of
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 173d18be6f..dff181dc2d 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -34317,6 +34317,63 @@ fullname="/home/nickrob/myprog.c",line="73",arch="i386:x86_64"@}
 (gdb)
 @end smallexample
 
+@subheading The @code{-complete} Command
+@findex -complete
+
+@subheading Synopsis
+
+@smallexample
+-complete @var{command}
+@end smallexample
+
+Show a list of completions for partially typed CLI @var{command}.
+
+This command is intended for @sc{gdb/mi} frontends that cannot use two separate
+CLI and MI channels - for example: because of lack of PTYs like on Windows or
+because @sc{gdb} is used remotely via a SSH connection.
+
+@subheading Result
+
+The result consists of two one or two fields:
+
+@table @samp
+@item completion
+This field contain a string to be completed on a command line, including
+@var{command} passed as parameter to @code{-complete} command. If nothing can
+be completed this field is omitted.
+
+@item matches
+This field contain (possibly empty) array of matches. It is always present.
+
+@end table
+
+@subheading @value{GDBN} Command
+
+The corresponding @value{GDBN} command is @samp{complete}.
+
+@subheading Example
+
+@smallexample
+(gdb)
+-complete br
+^done,completion="break",matches=["break","break-range"],max_completions_reached="0"
+(gdb)
+-complete "b ma"
+^done,completion="b ma",matches=["b madvise","b main"],max_completions_reached="0"
+(gdb)
+-complete "b push_b"
+^done,completion="b push_back(",
+      matches=["b A::push_back(void*)",
+               "b std::string::push_back(char)",
+               "b std::vector<int, std::allocator<int> >::push_back(int&&)"],
+      max_completions_reached="0"
+(gdb)
+-complete "nonexist"
+^done,matches=[],max_completions_reached="0"
+(gdb)
+
+@end smallexample
+
 @node Annotations
 @chapter @value{GDBN} Annotations
 
diff --git a/gdb/mi/mi-cmds.c b/gdb/mi/mi-cmds.c
index bb7c20c777..683e990cdf 100644
--- a/gdb/mi/mi-cmds.c
+++ b/gdb/mi/mi-cmds.c
@@ -23,6 +23,7 @@
 #include "mi-cmds.h"
 #include "mi-main.h"
 
+
 struct mi_cmd;
 static struct mi_cmd **lookup_table (const char *command);
 static void build_table (struct mi_cmd *commands);
@@ -75,6 +76,7 @@ static struct mi_cmd mi_cmds[] =
                    &mi_suppress_notification.breakpoint),
   DEF_MI_CMD_MI_1 ("catch-unload", mi_cmd_catch_unload,
                    &mi_suppress_notification.breakpoint),
+  DEF_MI_CMD_MI ("complete", mi_cmd_complete),
   DEF_MI_CMD_MI ("data-disassemble", mi_cmd_disassemble),
   DEF_MI_CMD_MI ("data-evaluate-expression", mi_cmd_data_evaluate_expression),
   DEF_MI_CMD_MI ("data-list-changed-registers",
diff --git a/gdb/mi/mi-cmds.h b/gdb/mi/mi-cmds.h
index af38363cfc..32bd747d43 100644
--- a/gdb/mi/mi-cmds.h
+++ b/gdb/mi/mi-cmds.h
@@ -125,6 +125,7 @@ extern mi_cmd_argv_ftype mi_cmd_var_update;
 extern mi_cmd_argv_ftype mi_cmd_enable_pretty_printing;
 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.  */
 
diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c
index 7176963845..d6ddfea78d 100644
--- a/gdb/mi/mi-main.c
+++ b/gdb/mi/mi-main.c
@@ -2709,6 +2709,55 @@ mi_cmd_trace_frame_collected (const char *command, char **argv, int argc)
   }
 }
 
+/* Implement the "-complete" command.  */
+
+void
+mi_cmd_complete (const char *command, char **argv, int argc)
+{
+  if (argc != 1)
+    {
+      error (_("Usage: -complete COMMAND"));
+    }
+  if (max_completions == 0)
+    {
+      error (_("max-completions is zero,"
+               " completion is disabled.\n"));
+    }
+
+  int quote_char = '\0';
+  const char *word;
+
+  completion_result result = complete (argv[0], &word, &quote_char);
+
+  std::string arg_prefix (argv[0], word - argv[0]);
+
+  struct ui_out *uiout = current_uiout;
+
+  if (result.number_matches > 0)
+    uiout->field_fmt ("completion", "%s%s", arg_prefix.c_str (), result.match_list[0]);
+
+  {
+    ui_out_emit_list completions_emitter (uiout, "matches");
+
+    if (result.number_matches == 1)
+      {
+        uiout->field_fmt(NULL, "%s%s", arg_prefix.c_str (), result.match_list[0]);
+      }
+    else
+      {
+        result.sort_match_list ();
+        for (size_t i = 0; i < result.number_matches; i++)
+          {
+            uiout->field_fmt(NULL, "%s%s", arg_prefix.c_str (),
+                                           result.match_list[i + 1]);
+          }
+      }
+  }
+  uiout->field_string ("max_completions_reached",
+                       result.number_matches == max_completions ? "1" : "0");
+}
+
+
 void
 _initialize_mi_main (void)
 {
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index 7d8c7908fe..03135837f8 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,3 +1,8 @@
+2019-01-28  Jan Vrany  <jan.vrany@fit.cvut.cz>
+
+	* gdb.mi/mi-complete.exp: New file.
+	* gdb.mi/mi-complete.cc: Likewise.
+
 2019-01-21  Alan Hayward  <alan.hayward@arm.com>
 	* gdb.base/infcall-nested-structs.exp: Test C++ in addition to C.
 
diff --git a/gdb/testsuite/gdb.mi/mi-complete.cc b/gdb/testsuite/gdb.mi/mi-complete.cc
new file mode 100644
index 0000000000..fc85057d69
--- /dev/null
+++ b/gdb/testsuite/gdb.mi/mi-complete.cc
@@ -0,0 +1,21 @@
+#include <vector>
+
+class A
+{
+public:
+  void push_back(void *value);
+};
+
+void A::push_back(void *value)
+{
+  /* nothing */
+}
+
+int main(int argc, char **argv)
+{
+    std::vector<int> v;
+    v.push_back(1);
+    A a;
+    a.push_back(&v);
+    return 0;
+}
\ No newline at end of file
diff --git a/gdb/testsuite/gdb.mi/mi-complete.exp b/gdb/testsuite/gdb.mi/mi-complete.exp
new file mode 100644
index 0000000000..e82c2bff40
--- /dev/null
+++ b/gdb/testsuite/gdb.mi/mi-complete.exp
@@ -0,0 +1,75 @@
+# Copyright 2018 Free Software Foundation, Inc.
+
+# 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/>.
+
+# Verify -data-evaluate-expression. There are really minimal tests.
+
+# The goal is not to test gdb functionality, which is done by other tests,
+# but to verify the correct output response to MI operations.
+#
+
+load_lib mi-support.exp
+set MIFLAGS "-i=mi"
+
+gdb_exit
+if [mi_gdb_start] {
+    continue
+}
+
+standard_testfile .cc
+
+if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug c++}] != "" } {
+     untested "failed to compile"
+     return -1
+}
+
+mi_run_to_main
+
+mi_gdb_test "1-complete br" \
+            "1\\^done,completion=\"break\",matches=\\\[.*\"break\",.*\"break-range\".*\\\],max_completions_reached=\"0\"" \
+            "-complete br"
+
+# Check empty completion list
+mi_gdb_test "5-complete bogus" \
+            "5\\^done,matches=\\\[\\\],max_completions_reached=\"0\"" \
+            "-complete bogus"
+
+# Check completions for commands with space
+mi_gdb_test "4-complete \"b mai\"" \
+            "4\\^done,completion=\"b main\",matches=\\\[.*\"b main\".*\\\],max_completions_reached=\"0\"" \
+            "-complete \"b mai\""
+
+# Check wildmatching
+mi_gdb_test "5-complete \"b push_ba\"" \
+            "5\\^done,completion=\"b push_back\\(\",matches=\\\[.*\"b A::push_back\\(void\\*\\)\".*\\\],max_completions_reached=\"0\"" \
+            "-complete \"b push_ba\" (wildmatching)"
+
+mi_gdb_test "-info-gdb-mi-command complete" \
+            "\\^done,command=\{exists=\"true\"\}" \
+            "-info-gdb-mi-command complete"
+
+# Limit max completions and check that max_completions_reached=\"0\" is set
+# to 1.
+send_gdb "set max-completions 1\n"
+
+mi_gdb_test "2-complete br" \
+            ".*2\\^done,completion=\"br\[A-Za-z0-9-\]+\",matches=\\\[\"br\[A-Za-z0-9-\]+\"\\\],max_completions_reached=\"1\"" \
+            "-complete br (max-completions 1)"
+
+# Disable completions and check an error is returned
+send_gdb "set max-completions 0\n"
+
+mi_gdb_test "3-complete br" \
+            ".*3\\^error,msg=\".*" \
+            "-complete br (max-completions 0)"
-- 
2.20.1

^ permalink raw reply	[flat|nested] 50+ messages in thread

* [PATCH v3 1/2] MI: extract command completion logic from complete_command()
  2019-01-28 12:41   ` [PATCH v2 0/2] " Jan Vrany
                       ` (4 preceding siblings ...)
  2019-03-04 14:52     ` [PATCH v3 2/2] " Jan Vrany
@ 2019-03-04 14:52     ` Jan Vrany
  2019-03-04 14:52     ` [PATCH v3 0/2] MI: Add new command -complete Jan Vrany
                       ` (9 subsequent siblings)
  15 siblings, 0 replies; 50+ messages in thread
From: Jan Vrany @ 2019-03-04 14:52 UTC (permalink / raw)
  To: gdb-patches; +Cc: Jan Vrany

Extract completion logic from CLI complete_command() into a new
helper function complete().

gdb/Changelog:

	* completer.h (complete): New function.
	* completer.c (complete): Likewise.
	* cli/cli-cmds.c: (complete_command): Update to use new complete()
	function defined in completer.h.
---
 gdb/ChangeLog      |  7 +++++++
 gdb/cli/cli-cmds.c | 30 +-----------------------------
 gdb/completer.c    | 36 ++++++++++++++++++++++++++++++++++++
 gdb/completer.h    |  7 +++++++
 4 files changed, 51 insertions(+), 29 deletions(-)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 8e03dbf883..be65f4a2d6 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,10 @@
+2019-01-24  Jan Vrany  <jan.vrany@fit.cvut.cz>
+
+	* completer.h (complete): New function.
+	* completer.c (complete): Likewise.
+	* cli/cli-cmds.c: (complete_command): Update to use new complete()
+	function defined in completer.h.
+
 2019-01-22  Philippe Waroquiers  <philippe.waroquiers@skynet.be>
 
 	* event-top.c (handle_line_of_input): use unique_xmalloc_ptr for
diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index 57cfad441c..bb0494719c 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -243,41 +243,13 @@ complete_command (const char *arg, int from_tty)
   if (arg == NULL)
     arg = "";
 
-  completion_tracker tracker_handle_brkchars;
-  completion_tracker tracker_handle_completions;
-  completion_tracker *tracker;
-
   int quote_char = '\0';
   const char *word;
 
-  TRY
-    {
-      word = completion_find_completion_word (tracker_handle_brkchars,
-					      arg, &quote_char);
-
-      /* Completers that provide a custom word point in the
-	 handle_brkchars phase also compute their completions then.
-	 Completers that leave the completion word handling to readline
-	 must be called twice.  */
-      if (tracker_handle_brkchars.use_custom_word_point ())
-	tracker = &tracker_handle_brkchars;
-      else
-	{
-	  complete_line (tracker_handle_completions, word, arg, strlen (arg));
-	  tracker = &tracker_handle_completions;
-	}
-    }
-  CATCH (ex, RETURN_MASK_ALL)
-    {
-      return;
-    }
-  END_CATCH
+  completion_result result = complete (arg, &word, &quote_char);
 
   std::string arg_prefix (arg, word - arg);
 
-  completion_result result
-    = tracker->build_completion_result (word, word - arg, strlen (arg));
-
   if (result.number_matches != 0)
     {
       if (result.number_matches == 1)
diff --git a/gdb/completer.c b/gdb/completer.c
index fed815a53c..e3d81d6226 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -1615,6 +1615,42 @@ make_completion_match_str (gdb::unique_xmalloc_ptr<char> &&match_name,
   return gdb::unique_xmalloc_ptr<char> (newobj);
 }
 
+/* See complete.h.  */
+
+completion_result
+complete (const char *line, char const **word, int *quote_char)
+{
+  completion_tracker tracker_handle_brkchars;
+  completion_tracker tracker_handle_completions;
+  completion_tracker *tracker;
+
+  TRY
+    {
+      *word = completion_find_completion_word (tracker_handle_brkchars,
+					      line, quote_char);
+
+      /* Completers that provide a custom word point in the
+	 handle_brkchars phase also compute their completions then.
+	 Completers that leave the completion word handling to readline
+	 must be called twice.  */
+      if (tracker_handle_brkchars.use_custom_word_point ())
+	tracker = &tracker_handle_brkchars;
+      else
+	{
+	  complete_line (tracker_handle_completions, *word, line, strlen (line));
+	  tracker = &tracker_handle_completions;
+	}
+    }
+  CATCH (ex, RETURN_MASK_ALL)
+    {
+      return {};
+    }
+  END_CATCH
+
+  return tracker->build_completion_result (*word, *word - line, strlen (line));
+}
+
+
 /* Generate completions all at once.  Does nothing if max_completions
    is 0.  If max_completions is non-negative, this will collect at
    most max_completions strings.
diff --git a/gdb/completer.h b/gdb/completer.h
index e81243a721..a46f8cf22a 100644
--- a/gdb/completer.h
+++ b/gdb/completer.h
@@ -510,6 +510,13 @@ extern void complete_line (completion_tracker &tracker,
 			   const char *line_buffer,
 			   int point);
 
+/* Complete LINE and return completion results.  For completion purposes,
+   cursor position is assumed to be at the end of LINE.  WORD is set to
+   the end of word to complete.  QUOTE_CHAR is set to the opening quote
+   character if we found an unclosed quoted substring, '\0' otherwise.  */
+extern completion_result
+  complete (const char *line, char const **word, int *quote_char);
+
 /* Find the bounds of the word in TEXT for completion purposes, and
    return a pointer to the end of the word.  Calls the completion
    machinery for a handle_brkchars phase (using TRACKER) to figure out
-- 
2.20.1

^ permalink raw reply	[flat|nested] 50+ messages in thread

* [PATCH v3 0/2] MI: Add new command -complete
  2019-01-28 12:41   ` [PATCH v2 0/2] " Jan Vrany
                       ` (5 preceding siblings ...)
  2019-03-04 14:52     ` [PATCH v3 1/2] MI: extract command completion logic from complete_command() Jan Vrany
@ 2019-03-04 14:52     ` Jan Vrany
  2019-04-18 11:59     ` [PATCH v4 1/2] MI: extract command completion logic from complete_command() Jan Vrany
                       ` (8 subsequent siblings)
  15 siblings, 0 replies; 50+ messages in thread
From: Jan Vrany @ 2019-03-04 14:52 UTC (permalink / raw)
  To: gdb-patches; +Cc: Jan Vrany

This is a rework of v2 patch based on Pedro's comments.

Differences v2 -> v3:

  * to support wildmatching, the command output had to change to
    provide both, "completion" (what a frontend should complete)
    and "matches" (a list of possible completions)
  * and some formatting issues.

Differences v1 -> v2:

  * extracted common completion logic to a new helper function
  * implemented MI command using a new mi-specific function rather
    than using CLI implementation.

Jan Vrany (2):
  MI: extract command completion logic from complete_command()
  MI: Add new command -complete

 gdb/ChangeLog                        | 14 ++++++
 gdb/NEWS                             |  7 +++
 gdb/cli/cli-cmds.c                   | 30 +----------
 gdb/completer.c                      | 36 +++++++++++++
 gdb/completer.h                      |  7 +++
 gdb/doc/ChangeLog                    |  5 ++
 gdb/doc/gdb.texinfo                  | 57 +++++++++++++++++++++
 gdb/mi/mi-cmds.c                     |  2 +
 gdb/mi/mi-cmds.h                     |  1 +
 gdb/mi/mi-main.c                     | 49 ++++++++++++++++++
 gdb/testsuite/ChangeLog              |  5 ++
 gdb/testsuite/gdb.mi/mi-complete.cc  | 21 ++++++++
 gdb/testsuite/gdb.mi/mi-complete.exp | 75 ++++++++++++++++++++++++++++
 13 files changed, 280 insertions(+), 29 deletions(-)
 create mode 100644 gdb/testsuite/gdb.mi/mi-complete.cc
 create mode 100644 gdb/testsuite/gdb.mi/mi-complete.exp

-- 
2.20.1

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH v3 2/2] MI: Add new command -complete
  2019-03-04 14:52     ` [PATCH v3 2/2] " Jan Vrany
@ 2019-03-04 17:35       ` Eli Zaretskii
  2019-04-03 19:23       ` Pedro Alves
  1 sibling, 0 replies; 50+ messages in thread
From: Eli Zaretskii @ 2019-03-04 17:35 UTC (permalink / raw)
  To: Jan Vrany; +Cc: gdb-patches

> From: Jan Vrany <jan.vrany@fit.cvut.cz>
> Cc: Jan Vrany <jan.vrany@fit.cvut.cz>
> Date: Mon,  4 Mar 2019 14:52:03 +0000
> 
> There is a CLI command 'complete' intended to use with emacs. Such a command
> would also be useful for MI frontends, when separate CLI and MI channels cannot
> be used. For example, on Windows (because of lack of PTYs) or when GDB is used
> through SSH session.
> 
> This commit adds a new '-complete' MI command.

Thanks.

> diff --git a/gdb/NEWS b/gdb/NEWS
> index eaef6aa384..3018313a46 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -96,6 +96,13 @@ maint show dwarf unwinders
>  info proc files
>    Display a list of open files for a process.
>  
> +* New MI commands
> +
> +-complete
> +  This lists all the possible completions for the rest of the line, if it
> +  were to be given as a command itself.  This is intended for use by MI frontends
> +  in cases when separate CLI and MI channels cannot be used.

This part is OK.

> +CLI and MI channels - for example: because of lack of PTYs like on Windows or
> +because @sc{gdb} is used remotely via a SSH connection.

We use "@value{GDBN}" instead of a literal "gdb" in the manual.

> +@item completion
> +This field contain a string to be completed on a command line, including
              ^^^^^^^
"contains"

> +@var{command} passed as parameter to @code{-complete} command. If nothing can
                                                                ^^
Two spaces between sentences, please.

> +be completed this field is omitted.
               ^
Comma missing here.

I find this text a bit confusing, so I suggest a slight rewording:

  This field contains the completed @var{command}.  If @var{command}
  has no known completions, this field is omitted.

> +@item matches
> +This field contain (possibly empty) array of matches. It is always present.
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
"contains a (possibly empty) list of matches"

> +@end table

What about the max_completions_reached field?  I see it here:

> +^done,completion="break",matches=["break","break-range"],max_completions_reached="0"

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH v2 0/2] MI: Add new command -complete
  2019-02-28 10:18             ` Jan Vrany
@ 2019-03-05 20:53               ` Pedro Alves
  2019-03-06 15:09                 ` Jan Vrany
  0 siblings, 1 reply; 50+ messages in thread
From: Pedro Alves @ 2019-03-05 20:53 UTC (permalink / raw)
  To: Jan Vrany, Tom Tromey; +Cc: gdb-patches, gdb

On 02/28/2019 10:18 AM, Jan Vrany wrote:
> Hello,
> 
> On Wed, 2019-02-27 at 20:41 +0000, Pedro Alves wrote:
>> I'm totally not against this new command at all, but I have to say that I'd be
>> much more thrilled if someone just spent the time to make separate CLI/MI
>> channels work on Windows too.  The channel doesn't _have_ to be a PTY.
> 
> I know. That was my initial idea just to use named pipes year or two back
> when I started to support Windows. However:
> 
> 1) Completion CLI is AFAIK implemented using readline which has problems 
>    working over pipes. Actually, I never got CLI working satisfactorily
>    on Windows even when I just run GDB "normally" from Windows command shell (`cmd.exe`),
>    let alone over pipes or alike. 

Curious.  AFAIK native Windows gdb over cmd.exe should work fine.

Over pipes or the like, indeed I wouldn't be surprised with issues.
"set interactive-mode on" may help.

BTW, (and I just remembered that after sending the previous email), AFAIK,
Eclipse made the GDB console (with new-ui) work in the Windows port by
using WinPTY.

> 
> 2) Even if it would work, there are still other usecases which working readline
>    and separate CLI/MI channel on Windows would not support. For example:
>    
>    (a) at the moment we use "my" frontend [1], [2] running on developers laptop 
>        connecting over ssh to a GDB that runs on remote RISC-V machine. Essentially,
>        instead of lauching gdb locally (on dev's machine) like: 
> 
>          gdb path/to/binary
> 
>        we do 
> 
>          ssh unleashed gdb -i=mi2 path/to/binary
> 
>        In this case, opening a secondary MI channel is very problematic. 

Why's that problematic?  You could forward a tcp port or a unix domain socket
over shh for MI?  Again, there's no real reason that new-ui only works with
ptys, other than noone ever tweaking the code to support other file types.

>        -complete command gives me at least a way to implement completion which
>        we found very important. There are still some quirks with that, sure, 
>        like when you run `pi` or `shell` commands it completely ruins the MI 
>        channel (this is a bug have not yet looked at let alone fixed but it is 
>        on my very long list).
> 

To me, it seems like you'll never manage to mimic gdb's behavior perfectly.
new-ui gets you perfect behavior, because, well, there's no mimicing.

>    (b) another frontend feature asked was to provide a kind of "workspace" for GDB commands,
>        This is essentially a text editor in which you write ad-hoc commands (possibly
>        with comments) and execute them by selecting portion of a text and pressing a button
>        (or with a shortcut). -complete command would allow me to implement completion
>        in such "workspace" 
> 

I see.

>        (I know, this may sound like a weird UI, but this is essentially what a Smalltalk workspace
>        is but for GDB commands. So far users of this frontend are mainly - me including -  
>        a small bunch of old smalltalkers working on virtual machines)
> 
> [1]: https://bitbucket.org/janvrany/jv-vdb/src/default/
> [2]: https://bitbucket.org/janvrany/jv-libgdbs/src/default/
> [3]: https://bitbucket.org/janvrany/jv-vdb/src/default/doc/Invoking.md
>    
>>
>> On 02/26/2019 07:49 PM, Tom Tromey wrote:
>>>>>>>> "Jan" == Jan Vrany <jan.vrany@fit.cvut.cz> writes:
>>>
>>> Jan> Are there any other GDB/MI users to comment on this? What would you
>>> Jan> prefer?
>>>
>>> Given the lack of response, I think you should just say which you
>>> prefer.  If you think it would be better the "other" way, go for it.
>>> Or if you'd rather the patches you already have, let me know.
>>
>> Jan, please consider the wildmatching case.  E.g., when debugging GDB itself:
>>
>> (gdb) b push_bac<TAB>
>> Display all 102 possibilities? (y or n)
>> debug_names::offset_vec_tmpl<unsigned int>::push_back_reorder(unsigned long)
>> debug_names::offset_vec_tmpl<unsigned long>::push_back_reorder(unsigned long)
>> std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::push_back(char)
>> ...
>>
>> The frontend needs to complete "b push_bac" -> "b push_back", and present
>> the matches.
>>
>> But the least common denominator is not at the start of the matches
>> strings.  How will a frontend compute the LCD from the matches list alone?
> 
> I see. I was not aware of this behavior, thanks for pointing this out! I'll address that
> in next iterations. 

Another thing that I remembered is that in some cases, GDB's completion actually
replaces the whole complete word, instead of just appending.  For example, try:

  (gdb) handle sigv<TAB>

Results in:

  (gdb) handle SIGVTALRM

ISTR having run into other examples, but not recalling
them right now.

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH v2 0/2] MI: Add new command -complete
  2019-03-05 20:53               ` Pedro Alves
@ 2019-03-06 15:09                 ` Jan Vrany
  2019-03-06 15:45                   ` Eli Zaretskii
  0 siblings, 1 reply; 50+ messages in thread
From: Jan Vrany @ 2019-03-06 15:09 UTC (permalink / raw)
  To: Pedro Alves, Tom Tromey; +Cc: gdb-patches, gdb

Hello, 

On Tue, 2019-03-05 at 20:53 +0000, Pedro Alves wrote:
> On 02/28/2019 10:18 AM, Jan Vrany wrote:
> > Hello,
> > 
> > On Wed, 2019-02-27 at 20:41 +0000, Pedro Alves wrote:
> > > I'm totally not against this new command at all, but I have to say that I'd be
> > > much more thrilled if someone just spent the time to make separate CLI/MI
> > > channels work on Windows too.  The channel doesn't _have_ to be a PTY.
> > 
> > I know. That was my initial idea just to use named pipes year or two back
> > when I started to support Windows. However:
> > 
> > 1) Completion CLI is AFAIK implemented using readline which has problems 
> >    working over pipes. Actually, I never got CLI working satisfactorily
> >    on Windows even when I just run GDB "normally" from Windows command shell (`cmd.exe`),
> >    let alone over pipes or alike. 
> 
> Curious.  AFAIK native Windows gdb over cmd.exe should work fine.

Maybe I just don't know how to compile it properly. I have just compiled
fresh c3734e093aab1ce from git (only with this patch 
https://sourceware.org/ml/gdb-patches/2018-10/msg00614.html to make it compile
with Python 3) using using MSYS2 MINGW64 toolchain on Windows 10. 
This is the exact configure command:

bash ../configure --build=x86_64-w64-mingw32 --disable-werror --with-guile=no --with-python=C:\msys64\mingw64\bin\python3 --enable-targets="i686-w64-mingw32,x86_64-w64-mingw32"

Completion by tab seem to work. 

Backspace practially does not, it deletes the character in the line 
buffer (presumably) but not on the screen. Instead, it moves the caret 
one character on the right. Therefore what use see on the screen is not 
what it sent to GDB when she presses enter. 

Moving cursor by left arrow followed by typing has similar issues. 
Same for delete. Same for pressing Ctrl-R for searching the history. 

Generally, internally the state of the buffer might be OK (hard to tell)
but what is shown on the screen is completely out of sync, making it really 
hard to work with, at least for me. 

Again, it might well be just that I don't know how to configure / compile 
things properly. In the past I tried pre-compiled GDBs from cygwin & msys2
various versions of each and each of them has similar issues so I give up
digging deeper. 


> 
> Over pipes or the like, indeed I wouldn't be surprised with issues.
> "set interactive-mode on" may help.
> 
> BTW, (and I just remembered that after sending the previous email), AFAIK,
> Eclipse made the GDB console (with new-ui) work in the Windows port by
> using WinPTY.

I was not aware of this. I'll have a look at eclipse code. Thanks!

> 
> > 2) Even if it would work, there are still other usecases which working readline
> >    and separate CLI/MI channel on Windows would not support. For example:
> >    
> >    (a) at the moment we use "my" frontend [1], [2] running on developers laptop 
> >        connecting over ssh to a GDB that runs on remote RISC-V machine. Essentially,
> >        instead of lauching gdb locally (on dev's machine) like: 
> > 
> >          gdb path/to/binary
> > 
> >        we do 
> > 
> >          ssh unleashed gdb -i=mi2 path/to/binary
> > 
> >        In this case, opening a secondary MI channel is very problematic. 
> 
> Why's that problematic?  You could forward a tcp port or a unix domain socket
> over shh for MI?  Again, there's no real reason that new-ui only works with
> ptys, other than noone ever tweaking the code to support other file types.
> 

This could be a way, yes. I just find it little too fiddly to implement. 
Using ssh command has some problems, using things like libssh has other problems.
Little details, but that's where the devil is. Plus it does not solve the
issues on Windows I described above nor the would it help me with the workspace.
So it seems to me that -complete would be a reasonable way to deal with all that. 

> >        -complete command gives me at least a way to implement completion which
> >        we found very important. There are still some quirks with that, sure, 
> >        like when you run `pi` or `shell` commands it completely ruins the MI 
> >        channel (this is a bug have not yet looked at let alone fixed but it is 
> >        on my very long list).
> > 
> 
> To me, it seems like you'll never manage to mimic gdb's behavior perfectly.
> new-ui gets you perfect behavior, because, well, there's no mimicing.
> 

Well, I'm not seeking to have perfect imitation of gdb's behavior. Just good enough. 

> >    (b) another frontend feature asked was to provide a kind of "workspace" for GDB commands,
> >        This is essentially a text editor in which you write ad-hoc commands (possibly
> >        with comments) and execute them by selecting portion of a text and pressing a button
> >        (or with a shortcut). -complete command would allow me to implement completion
> >        in such "workspace" 
> > 
> 
> I see.
> 
> >        (I know, this may sound like a weird UI, but this is essentially what a Smalltalk workspace
> >        is but for GDB commands. So far users of this frontend are mainly - me including -  
> >        a small bunch of old smalltalkers working on virtual machines)
> > 
> > [1]: https://bitbucket.org/janvrany/jv-vdb/src/default/
> > [2]: https://bitbucket.org/janvrany/jv-libgdbs/src/default/
> > [3]: https://bitbucket.org/janvrany/jv-vdb/src/default/doc/Invoking.md
> >    
> > > On 02/26/2019 07:49 PM, Tom Tromey wrote:
> > > > > > > > > "Jan" == Jan Vrany <jan.vrany@fit.cvut.cz> writes:
> > > > 
> > > > Jan> Are there any other GDB/MI users to comment on this? What would you
> > > > Jan> prefer?
> > > > 
> > > > Given the lack of response, I think you should just say which you
> > > > prefer.  If you think it would be better the "other" way, go for it.
> > > > Or if you'd rather the patches you already have, let me know.
> > > 
> > > Jan, please consider the wildmatching case.  E.g., when debugging GDB itself:
> > > 
> > > (gdb) b push_bac<TAB>
> > > Display all 102 possibilities? (y or n)
> > > debug_names::offset_vec_tmpl<unsigned int>::push_back_reorder(unsigned long)
> > > debug_names::offset_vec_tmpl<unsigned long>::push_back_reorder(unsigned long)
> > > std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::push_back(char)
> > > ...
> > > 
> > > The frontend needs to complete "b push_bac" -> "b push_back", and present
> > > the matches.
> > > 
> > > But the least common denominator is not at the start of the matches
> > > strings.  How will a frontend compute the LCD from the matches list alone?
> > 
> > I see. I was not aware of this behavior, thanks for pointing this out! I'll address that
> > in next iterations. 
> 
> Another thing that I remembered is that in some cases, GDB's completion actually
> replaces the whole complete word, instead of just appending.  For example, try:
> 
>   (gdb) handle sigv<TAB>
> 
> Results in:
> 
>   (gdb) handle SIGVTALRM
> 
> ISTR having run into other examples, but not recalling
> them right now.

I think this is handled by v3:

(gdb) 
-complete "handle sigv"
^done,completion="handle SIGVTALRM",matches=["handle SIGVTALRM"],max_completions_reached="0"
(gdb) 


Anyways, another way to look at this commit is that there's already 
CLI command "complete" and this commit "merely" add an MI counterpart of it,
exposing over MI what's already available in CLI. 

If the maintenance cost -complete is considered "too much for little gain", let's 
drop it.  

Thanks! 

Jan

> 
> Thanks,
> Pedro Alves

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH v2 0/2] MI: Add new command -complete
  2019-03-06 15:09                 ` Jan Vrany
@ 2019-03-06 15:45                   ` Eli Zaretskii
  2019-03-06 16:37                     ` Jan Vrany
  0 siblings, 1 reply; 50+ messages in thread
From: Eli Zaretskii @ 2019-03-06 15:45 UTC (permalink / raw)
  To: Jan Vrany; +Cc: palves, tom, gdb-patches, gdb

> From: Jan Vrany <jan.vrany@fit.cvut.cz>
> Cc: gdb-patches@sourceware.org, "gdb@sourceware.org" <gdb@sourceware.org>
> Date: Wed, 06 Mar 2019 15:09:33 +0000
> 
> > > 1) Completion CLI is AFAIK implemented using readline which has problems 
> > >    working over pipes. Actually, I never got CLI working satisfactorily
> > >    on Windows even when I just run GDB "normally" from Windows command shell (`cmd.exe`),
> > >    let alone over pipes or alike. 
> > 
> > Curious.  AFAIK native Windows gdb over cmd.exe should work fine.
> 
> Maybe I just don't know how to compile it properly. I have just compiled
> fresh c3734e093aab1ce from git (only with this patch 
> https://sourceware.org/ml/gdb-patches/2018-10/msg00614.html to make it compile
> with Python 3) using using MSYS2 MINGW64 toolchain on Windows 10. 
> This is the exact configure command:
> 
> bash ../configure --build=x86_64-w64-mingw32 --disable-werror --with-guile=no --with-python=C:\msys64\mingw64\bin\python3 --enable-targets="i686-w64-mingw32,x86_64-w64-mingw32"
> 
> Completion by tab seem to work. 
> 
> Backspace practially does not, it deletes the character in the line 
> buffer (presumably) but not on the screen. Instead, it moves the caret 
> one character on the right. Therefore what use see on the screen is not 
> what it sent to GDB when she presses enter. 
> 
> Moving cursor by left arrow followed by typing has similar issues. 
> Same for delete. Same for pressing Ctrl-R for searching the history. 

How did you invoke GDB from cmd.exe, to make these problems appear?
Can you show your exact invocation command line?

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH v2 0/2] MI: Add new command -complete
  2019-03-06 15:45                   ` Eli Zaretskii
@ 2019-03-06 16:37                     ` Jan Vrany
  2019-03-06 17:36                       ` Eli Zaretskii
  0 siblings, 1 reply; 50+ messages in thread
From: Jan Vrany @ 2019-03-06 16:37 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: palves, tom, gdb-patches, gdb

On Wed, 2019-03-06 at 17:45 +0200, Eli Zaretskii wrote:
> > From: Jan Vrany <jan.vrany@fit.cvut.cz>
> > Cc: gdb-patches@sourceware.org, "gdb@sourceware.org" <gdb@sourceware.org>
> > Date: Wed, 06 Mar 2019 15:09:33 +0000
> > 
> > > > 1) Completion CLI is AFAIK implemented using readline which has problems 
> > > >    working over pipes. Actually, I never got CLI working satisfactorily
> > > >    on Windows even when I just run GDB "normally" from Windows command shell (`cmd.exe`),
> > > >    let alone over pipes or alike. 
> > > 
> > > Curious.  AFAIK native Windows gdb over cmd.exe should work fine.
> > 
> > Maybe I just don't know how to compile it properly. I have just compiled
> > fresh c3734e093aab1ce from git (only with this patch 
> > https://sourceware.org/ml/gdb-patches/2018-10/msg00614.html to make it compile
> > with Python 3) using using MSYS2 MINGW64 toolchain on Windows 10. 
> > This is the exact configure command:
> > 
> > bash ../configure --build=x86_64-w64-mingw32 --disable-werror --with-guile=no --with-python=C:\msys64\mingw64\bin\python3 --enable-targets="i686-w64-mingw32,x86_64-w64-mingw32"
> > 
> > Completion by tab seem to work. 
> > 
> > Backspace practially does not, it deletes the character in the line 
> > buffer (presumably) but not on the screen. Instead, it moves the caret 
> > one character on the right. Therefore what use see on the screen is not 
> > what it sent to GDB when she presses enter. 
> > 
> > Moving cursor by left arrow followed by typing has similar issues. 
> > Same for delete. Same for pressing Ctrl-R for searching the history. 
> 
> How did you invoke GDB from cmd.exe, to make these problems appear?
> Can you show your exact invocation command line?

Just like:

H:\Projects\gdb\master\build-x86_64-msys2\gdb>gdb.exe

HTH, 

Jan


^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH v2 0/2] MI: Add new command -complete
  2019-03-06 16:37                     ` Jan Vrany
@ 2019-03-06 17:36                       ` Eli Zaretskii
  0 siblings, 0 replies; 50+ messages in thread
From: Eli Zaretskii @ 2019-03-06 17:36 UTC (permalink / raw)
  To: Jan Vrany; +Cc: palves, tom, gdb-patches, gdb

> From: Jan Vrany <jan.vrany@fit.cvut.cz>
> Cc: palves@redhat.com, tom@tromey.com, gdb-patches@sourceware.org,
>         gdb@sourceware.org
> Date: Wed, 06 Mar 2019 16:36:39 +0000
> 
> > > Completion by tab seem to work. 
> > > 
> > > Backspace practially does not, it deletes the character in the line 
> > > buffer (presumably) but not on the screen. Instead, it moves the caret 
> > > one character on the right. Therefore what use see on the screen is not 
> > > what it sent to GDB when she presses enter. 
> > > 
> > > Moving cursor by left arrow followed by typing has similar issues. 
> > > Same for delete. Same for pressing Ctrl-R for searching the history. 
> > 
> > How did you invoke GDB from cmd.exe, to make these problems appear?
> > Can you show your exact invocation command line?
> 
> Just like:
> 
> H:\Projects\gdb\master\build-x86_64-msys2\gdb>gdb.exe

But this doesn't create any pipes to communicate with GDB.  Instead,
this communicates via the default stdin/stdout connected to the
console, and GDB should recognize it as such.  So all the problems you
describe should not happen, and indeed don't happen for me when I
invoke GDB from cmd.exe.

Do you perhaps have a ~/.inputrc file, or some other local
customization, which might affect how Readline works?  because what
you describe surely sounds like broken Readline.

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH v3 2/2] MI: Add new command -complete
  2019-03-04 14:52     ` [PATCH v3 2/2] " Jan Vrany
  2019-03-04 17:35       ` Eli Zaretskii
@ 2019-04-03 19:23       ` Pedro Alves
  1 sibling, 0 replies; 50+ messages in thread
From: Pedro Alves @ 2019-04-03 19:23 UTC (permalink / raw)
  To: Jan Vrany, gdb-patches

Hi Jan,

This version generally looks good to me.  There are a few nits
to address, but with those picked, this should be good to go.

On 03/04/2019 02:52 PM, Jan Vrany wrote:

> diff --git a/gdb/NEWS b/gdb/NEWS
> index eaef6aa384..3018313a46 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -96,6 +96,13 @@ maint show dwarf unwinders
>  info proc files
>    Display a list of open files for a process.
>  
> +* New MI commands
> +
> +-complete
> +  This lists all the possible completions for the rest of the line, if it
> +  were to be given as a command itself.  This is intended for use by MI frontends
> +  in cases when separate CLI and MI channels cannot be used.

That line looks too long.  Hit M-q in emacs to reflow it.

> --- a/gdb/mi/mi-cmds.c
> +++ b/gdb/mi/mi-cmds.c
> @@ -23,6 +23,7 @@
>  #include "mi-cmds.h"
>  #include "mi-main.h"
>  
> +

Spurious space here.  Please drop it.

>  struct mi_cmd;
>  static struct mi_cmd **lookup_table (const char *command);
>  static void build_table (struct mi_cmd *commands);
> @@ -75,6 +76,7 @@ static struct mi_cmd mi_cmds[] =

> --- a/gdb/mi/mi-main.c
> +++ b/gdb/mi/mi-main.c
> @@ -2709,6 +2709,55 @@ mi_cmd_trace_frame_collected (const char *command, char **argv, int argc)
>    }
>  }
>  
> +/* Implement the "-complete" command.  */
> +
> +void
> +mi_cmd_complete (const char *command, char **argv, int argc)
> +{
> +  if (argc != 1)
> +    {
> +      error (_("Usage: -complete COMMAND"));
> +    }

We don't wrap single-line statements with {}'s.

> +  if (max_completions == 0)
> +    {
> +      error (_("max-completions is zero,"
> +               " completion is disabled.\n"));
> +    }
> +
> +  int quote_char = '\0';
> +  const char *word;
> +
> +  completion_result result = complete (argv[0], &word, &quote_char);
> +
> +  std::string arg_prefix (argv[0], word - argv[0]);
> +
> +  struct ui_out *uiout = current_uiout;
> +
> +  if (result.number_matches > 0)
> +    uiout->field_fmt ("completion", "%s%s", arg_prefix.c_str (), result.match_list[0]);
> +
> +  {
> +    ui_out_emit_list completions_emitter (uiout, "matches");
> +
> +    if (result.number_matches == 1)
> +      {
> +        uiout->field_fmt(NULL, "%s%s", arg_prefix.c_str (), result.match_list[0]);
> +      }

Ditto.  

Missing space before parens in "field_fmt(NULL".

Watch out for too-long lines -- 80 cols is the hard max.

> +    else
> +      {
> +        result.sort_match_list ();
> +        for (size_t i = 0; i < result.number_matches; i++)
> +          {
> +            uiout->field_fmt(NULL, "%s%s", arg_prefix.c_str (),
> +                                           result.match_list[i + 1]);

Missing space before parens in "field_fmt(NULL".

> +          }
> +      }
> +  }
> +  uiout->field_string ("max_completions_reached",
> +                       result.number_matches == max_completions ? "1" : "0");
> +}
> +
> +
>  void
>  _initialize_mi_main (void)
>  {
> diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
> index 7d8c7908fe..03135837f8 100644
> --- a/gdb/testsuite/ChangeLog
> +++ b/gdb/testsuite/ChangeLog
> @@ -1,3 +1,8 @@
> +2019-01-28  Jan Vrany  <jan.vrany@fit.cvut.cz>
> +
> +	* gdb.mi/mi-complete.exp: New file.
> +	* gdb.mi/mi-complete.cc: Likewise.
> +
>  2019-01-21  Alan Hayward  <alan.hayward@arm.com>
>  	* gdb.base/infcall-nested-structs.exp: Test C++ in addition to C.
>  
> diff --git a/gdb/testsuite/gdb.mi/mi-complete.cc b/gdb/testsuite/gdb.mi/mi-complete.cc
> new file mode 100644
> index 0000000000..fc85057d69
> --- /dev/null
> +++ b/gdb/testsuite/gdb.mi/mi-complete.cc
> @@ -0,0 +1,21 @@
> +#include <vector>

Missing copyright header.

> +
> +class A
> +{
> +public:
> +  void push_back(void *value);
> +};
> +
> +void A::push_back(void *value)
> +{
> +  /* nothing */
> +}
> +
> +int main(int argc, char **argv)
> +{
> +    std::vector<int> v;
> +    v.push_back(1);
> +    A a;
> +    a.push_back(&v);
> +    return 0;
> +}

Please adjust the formatting to follow GNU conventions.

> \ No newline at end of file

Please add the missing newline.

> diff --git a/gdb/testsuite/gdb.mi/mi-complete.exp b/gdb/testsuite/gdb.mi/mi-complete.exp
> new file mode 100644
> index 0000000000..e82c2bff40
> --- /dev/null
> +++ b/gdb/testsuite/gdb.mi/mi-complete.exp
> @@ -0,0 +1,75 @@
> +# Copyright 2018 Free Software Foundation, Inc.

This needs to be 2018-2019 now.

> +
> +# 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/>.
> +
> +# Verify -data-evaluate-expression. There are really minimal tests.

Please replace this with a description of what this testcase is about.

> +
> +# The goal is not to test gdb functionality, which is done by other tests,
> +# but to verify the correct output response to MI operations.
> +#

Drop this.

> +
> +load_lib mi-support.exp
> +set MIFLAGS "-i=mi"
> +
> +gdb_exit
> +if [mi_gdb_start] {
> +    continue
> +}
> +
> +standard_testfile .cc
> +
> +if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug c++}] != "" } {

Spurious double space after "if".

> +     untested "failed to compile"
> +     return -1
> +}
> +
> +mi_run_to_main
> +
> +mi_gdb_test "1-complete br" \
> +            "1\\^done,completion=\"break\",matches=\\\[.*\"break\",.*\"break-range\".*\\\],max_completions_reached=\"0\"" \
> +            "-complete br"
> +
> +# Check empty completion list

Write complete sentences -- add the missing period.

> +mi_gdb_test "5-complete bogus" \
> +            "5\\^done,matches=\\\[\\\],max_completions_reached=\"0\"" \
> +            "-complete bogus"
> +
> +# Check completions for commands with space

Missing period.

> +mi_gdb_test "4-complete \"b mai\"" \
> +            "4\\^done,completion=\"b main\",matches=\\\[.*\"b main\".*\\\],max_completions_reached=\"0\"" \
> +            "-complete \"b mai\""
> +
> +# Check wildmatching

Missing period.

> +mi_gdb_test "5-complete \"b push_ba\"" \
> +            "5\\^done,completion=\"b push_back\\(\",matches=\\\[.*\"b A::push_back\\(void\\*\\)\".*\\\],max_completions_reached=\"0\"" \
> +            "-complete \"b push_ba\" (wildmatching)"

Please see:

 https://sourceware.org/gdb/wiki/GDBTestcaseCookbook?highlight=%28testcase%29#Do_not_use_.22tail_parentheses.22_on_test_messages


> +
> +mi_gdb_test "-info-gdb-mi-command complete" \
> +            "\\^done,command=\{exists=\"true\"\}" \
> +            "-info-gdb-mi-command complete"
> +
> +# Limit max completions and check that max_completions_reached=\"0\" is set
> +# to 1.
> +send_gdb "set max-completions 1\n"
> +
> +mi_gdb_test "2-complete br" \
> +            ".*2\\^done,completion=\"br\[A-Za-z0-9-\]+\",matches=\\\[\"br\[A-Za-z0-9-\]+\"\\\],max_completions_reached=\"1\"" \
> +            "-complete br (max-completions 1)"
> +
> +# Disable completions and check an error is returned
> +send_gdb "set max-completions 0\n"
> +
> +mi_gdb_test "3-complete br" \
> +            ".*3\\^error,msg=\".*" \
> +            "-complete br (max-completions 0)"
> 

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 50+ messages in thread

* [PATCH v4 1/2] MI: extract command completion logic from complete_command()
  2019-01-28 12:41   ` [PATCH v2 0/2] " Jan Vrany
                       ` (6 preceding siblings ...)
  2019-03-04 14:52     ` [PATCH v3 0/2] MI: Add new command -complete Jan Vrany
@ 2019-04-18 11:59     ` Jan Vrany
  2019-04-18 11:59     ` [PATCH v4 2/2] MI: Add new command -complete Jan Vrany
                       ` (7 subsequent siblings)
  15 siblings, 0 replies; 50+ messages in thread
From: Jan Vrany @ 2019-04-18 11:59 UTC (permalink / raw)
  To: gdb-patches; +Cc: Jan Vrany

Extract completion logic from CLI complete_command() into a new
helper function complete().

gdb/Changelog:

	* completer.h (complete): New function.
	* completer.c (complete): Likewise.
	* cli/cli-cmds.c: (complete_command): Update to use new complete()
	function defined in completer.h.
---
 gdb/ChangeLog      |  7 +++++++
 gdb/cli/cli-cmds.c | 29 +----------------------------
 gdb/completer.c    | 35 +++++++++++++++++++++++++++++++++++
 gdb/completer.h    |  7 +++++++
 4 files changed, 50 insertions(+), 28 deletions(-)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index ef77fdbb5c..f2b0c16ec5 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,10 @@
+2019-01-24  Jan Vrany  <jan.vrany@fit.cvut.cz>
+
+	* completer.h (complete): New function.
+	* completer.c (complete): Likewise.
+	* cli/cli-cmds.c: (complete_command): Update to use new complete()
+	function defined in completer.h.
+
 2019-04-17  Tom Tromey  <tromey@adacore.com>
 
 	* dwarf2read.c (dwarf2_init_complex_target_type): Check "tt"
diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index 8480ba355c..aa9905308c 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -243,40 +243,13 @@ complete_command (const char *arg, int from_tty)
   if (arg == NULL)
     arg = "";
 
-  completion_tracker tracker_handle_brkchars;
-  completion_tracker tracker_handle_completions;
-  completion_tracker *tracker;
-
   int quote_char = '\0';
   const char *word;
 
-  try
-    {
-      word = completion_find_completion_word (tracker_handle_brkchars,
-					      arg, &quote_char);
-
-      /* Completers that provide a custom word point in the
-	 handle_brkchars phase also compute their completions then.
-	 Completers that leave the completion word handling to readline
-	 must be called twice.  */
-      if (tracker_handle_brkchars.use_custom_word_point ())
-	tracker = &tracker_handle_brkchars;
-      else
-	{
-	  complete_line (tracker_handle_completions, word, arg, strlen (arg));
-	  tracker = &tracker_handle_completions;
-	}
-    }
-  catch (const gdb_exception &ex)
-    {
-      return;
-    }
+  completion_result result = complete (arg, &word, &quote_char);
 
   std::string arg_prefix (arg, word - arg);
 
-  completion_result result
-    = tracker->build_completion_result (word, word - arg, strlen (arg));
-
   if (result.number_matches != 0)
     {
       if (result.number_matches == 1)
diff --git a/gdb/completer.c b/gdb/completer.c
index 5d1decca1d..cc2f80bc66 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -1613,6 +1613,41 @@ make_completion_match_str (gdb::unique_xmalloc_ptr<char> &&match_name,
   return gdb::unique_xmalloc_ptr<char> (newobj);
 }
 
+/* See complete.h.  */
+
+completion_result
+complete (const char *line, char const **word, int *quote_char)
+{
+  completion_tracker tracker_handle_brkchars;
+  completion_tracker tracker_handle_completions;
+  completion_tracker *tracker;
+
+  try
+    {
+      *word = completion_find_completion_word (tracker_handle_brkchars,
+					      line, quote_char);
+
+      /* Completers that provide a custom word point in the
+	 handle_brkchars phase also compute their completions then.
+	 Completers that leave the completion word handling to readline
+	 must be called twice.  */
+      if (tracker_handle_brkchars.use_custom_word_point ())
+	tracker = &tracker_handle_brkchars;
+      else
+	{
+	  complete_line (tracker_handle_completions, *word, line, strlen (line));
+	  tracker = &tracker_handle_completions;
+	}
+    }
+  catch (const gdb_exception &ex)
+    {
+      return {};
+    }
+
+  return tracker->build_completion_result (*word, *word - line, strlen (line));
+}
+
+
 /* Generate completions all at once.  Does nothing if max_completions
    is 0.  If max_completions is non-negative, this will collect at
    most max_completions strings.
diff --git a/gdb/completer.h b/gdb/completer.h
index 52f8d7dff4..38a0132ca4 100644
--- a/gdb/completer.h
+++ b/gdb/completer.h
@@ -510,6 +510,13 @@ extern void complete_line (completion_tracker &tracker,
 			   const char *line_buffer,
 			   int point);
 
+/* Complete LINE and return completion results.  For completion purposes,
+   cursor position is assumed to be at the end of LINE.  WORD is set to
+   the end of word to complete.  QUOTE_CHAR is set to the opening quote
+   character if we found an unclosed quoted substring, '\0' otherwise.  */
+extern completion_result
+  complete (const char *line, char const **word, int *quote_char);
+
 /* Find the bounds of the word in TEXT for completion purposes, and
    return a pointer to the end of the word.  Calls the completion
    machinery for a handle_brkchars phase (using TRACKER) to figure out
-- 
2.20.1

^ permalink raw reply	[flat|nested] 50+ messages in thread

* [PATCH v4 2/2] MI: Add new command -complete
  2019-01-28 12:41   ` [PATCH v2 0/2] " Jan Vrany
                       ` (7 preceding siblings ...)
  2019-04-18 11:59     ` [PATCH v4 1/2] MI: extract command completion logic from complete_command() Jan Vrany
@ 2019-04-18 11:59     ` Jan Vrany
  2019-04-18 12:51       ` Eli Zaretskii
  2019-04-18 14:15       ` Pedro Alves
  2019-04-18 11:59     ` [PATCH v4 0/2] " Jan Vrany
                       ` (6 subsequent siblings)
  15 siblings, 2 replies; 50+ messages in thread
From: Jan Vrany @ 2019-04-18 11:59 UTC (permalink / raw)
  To: gdb-patches; +Cc: Jan Vrany

There is a CLI command 'complete' intended to use with emacs. Such a command
would also be useful for MI frontends, when separate CLI and MI channels cannot
be used. For example, on Windows (because of lack of PTYs) or when GDB is used
through SSH session.

This commit adds a new '-complete' MI command.

gdb/Changelog:
2019-01-28  Jan Vrany  <jan.vrany@fit.cvut.cz>

	* mi/mi-cmds.h (mi_cmd_complete): New function.
	* mi/mi-main.c (mi_cmd_complete): Likewise.
	* mi/mi-cmds.c: Define new MI command -complete.
	* NEWS: Mention new -complete command.

gdb/doc/ChangeLog:
2019-01-28  Jan Vrany  <jan.vrany@fit.cvut.cz>

	* gdb.texinfo (Miscellaneous GDB/MI Commands): Document new
	MI command -complete.

gdb/testsuite/ChangeLog:
2019-01-28  Jan Vrany  <jan.vrany@fit.cvut.cz>

	* gdb.mi/mi-complete.exp: New file.
	* gdb.mi/mi-complete.cc: Likewise.
---
 gdb/ChangeLog                        |  7 +++
 gdb/NEWS                             |  7 +++
 gdb/doc/ChangeLog                    |  5 ++
 gdb/doc/gdb.texinfo                  | 61 +++++++++++++++++++++++
 gdb/mi/mi-cmds.c                     |  1 +
 gdb/mi/mi-cmds.h                     |  1 +
 gdb/mi/mi-main.c                     | 45 +++++++++++++++++
 gdb/testsuite/ChangeLog              |  5 ++
 gdb/testsuite/gdb.mi/mi-complete.cc  | 40 ++++++++++++++++
 gdb/testsuite/gdb.mi/mi-complete.exp | 72 ++++++++++++++++++++++++++++
 10 files changed, 244 insertions(+)
 create mode 100644 gdb/testsuite/gdb.mi/mi-complete.cc
 create mode 100644 gdb/testsuite/gdb.mi/mi-complete.exp

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index f2b0c16ec5..87fa43dd06 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,10 @@
+2019-01-28  Jan Vrany  <jan.vrany@fit.cvut.cz>
+
+	* mi/mi-cmds.h (mi_cmd_complete): New function.
+	* mi/mi-main.c (mi_cmd_complete): Likewise.
+	* mi/mi-cmds.c: Define new MI command -complete.
+	* NEWS: Mention new -complete command.
+
 2019-01-24  Jan Vrany  <jan.vrany@fit.cvut.cz>
 
 	* completer.h (complete): New function.
diff --git a/gdb/NEWS b/gdb/NEWS
index 5309a8f923..7aa31ccb17 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -27,6 +27,13 @@
      'array_indexes', 'symbols', 'unions', 'deref_refs', 'actual_objects',
      'static_members', 'max_elements', 'repeat_threshold', and 'format'.
 
+* New MI commands
+
+-complete
+  This lists all the possible completions for the rest of the line, if it
+  were to be given as a command itself.  This is intended for use by MI 
+  frontends in cases when separate CLI and MI channels cannot be used.
+
 *** Changes in GDB 8.3
 
 * GDB and GDBserver now support access to additional registers on
diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog
index 64073278fa..6f7a48787e 100644
--- a/gdb/doc/ChangeLog
+++ b/gdb/doc/ChangeLog
@@ -1,3 +1,8 @@
+2019-01-28  Jan Vrany  <jan.vrany@fit.cvut.cz>
+
+	* gdb.texinfo (Miscellaneous GDB/MI Commands): Document new
+	MI command -complete.
+
 2019-04-17  Alan Hayward  <alan.hayward@arm.com>
 
 	* gdb.texinfo (Other Command-Line Arguments for gdbserver)
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index a3a5f3e28c..52b203fd90 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -34438,6 +34438,67 @@ fullname="/home/nickrob/myprog.c",line="73",arch="i386:x86_64"@}
 (gdb)
 @end smallexample
 
+@subheading The @code{-complete} Command
+@findex -complete
+
+@subheading Synopsis
+
+@smallexample
+-complete @var{command}
+@end smallexample
+
+Show a list of completions for partially typed CLI @var{command}.
+
+This command is intended for @sc{gdb/mi} frontends that cannot use two separate
+CLI and MI channels - for example: because of lack of PTYs like on Windows or
+because @value{GDBN} is used remotely via a SSH connection.
+
+@subheading Result
+
+The result consists of two one or two fields:
+
+@table @samp
+@item completion
+This field contains the completed @var{command}.  If @var{command}
+has no known completions, this field is omitted.
+
+@item matches
+This field contains a (possibly empty) array of matches.  It is always present.
+
+@item max_completions_reached
+This field contains @code{1} if number of known completions is above
+@code{max-completions} limit (see @ref{Completion}), otherwise it contains
+@code{0}.  It is always present.
+
+@end table
+
+@subheading @value{GDBN} Command
+
+The corresponding @value{GDBN} command is @samp{complete}.
+
+@subheading Example
+
+@smallexample
+(gdb)
+-complete br
+^done,completion="break",matches=["break","break-range"],max_completions_reached="0"
+(gdb)
+-complete "b ma"
+^done,completion="b ma",matches=["b madvise","b main"],max_completions_reached="0"
+(gdb)
+-complete "b push_b"
+^done,completion="b push_back(",
+      matches=["b A::push_back(void*)",
+               "b std::string::push_back(char)",
+               "b std::vector<int, std::allocator<int> >::push_back(int&&)"],
+      max_completions_reached="0"
+(gdb)
+-complete "nonexist"
+^done,matches=[],max_completions_reached="0"
+(gdb)
+
+@end smallexample
+
 @node Annotations
 @chapter @value{GDBN} Annotations
 
diff --git a/gdb/mi/mi-cmds.c b/gdb/mi/mi-cmds.c
index fe30ac2e82..bbc0e2bd93 100644
--- a/gdb/mi/mi-cmds.c
+++ b/gdb/mi/mi-cmds.c
@@ -75,6 +75,7 @@ static struct mi_cmd mi_cmds[] =
                    &mi_suppress_notification.breakpoint),
   DEF_MI_CMD_MI_1 ("catch-unload", mi_cmd_catch_unload,
                    &mi_suppress_notification.breakpoint),
+  DEF_MI_CMD_MI ("complete", mi_cmd_complete),
   DEF_MI_CMD_MI ("data-disassemble", mi_cmd_disassemble),
   DEF_MI_CMD_MI ("data-evaluate-expression", mi_cmd_data_evaluate_expression),
   DEF_MI_CMD_MI ("data-list-changed-registers",
diff --git a/gdb/mi/mi-cmds.h b/gdb/mi/mi-cmds.h
index 7b22ce7813..58aa2d6f77 100644
--- a/gdb/mi/mi-cmds.h
+++ b/gdb/mi/mi-cmds.h
@@ -125,6 +125,7 @@ extern mi_cmd_argv_ftype mi_cmd_var_update;
 extern mi_cmd_argv_ftype mi_cmd_enable_pretty_printing;
 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.  */
 
diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c
index 17ca807471..0d8a27e6cc 100644
--- a/gdb/mi/mi-main.c
+++ b/gdb/mi/mi-main.c
@@ -2726,6 +2726,51 @@ mi_multi_location_breakpoint_output_fixed (ui_out *uiout)
   return mi_uiout->version () >= 3 || fix_multi_location_breakpoint_output;
 }
 
+/* Implement the "-complete" command.  */
+
+void
+mi_cmd_complete (const char *command, char **argv, int argc)
+{
+  if (argc != 1)
+    error (_("Usage: -complete COMMAND"));
+
+  if (max_completions == 0)
+    error (_("max-completions is zero, completion is disabled.\n"));
+
+  int quote_char = '\0';
+  const char *word;
+
+  completion_result result = complete (argv[0], &word, &quote_char);
+
+  std::string arg_prefix (argv[0], word - argv[0]);
+
+  struct ui_out *uiout = current_uiout;
+
+  if (result.number_matches > 0)
+    uiout->field_fmt ("completion", "%s%s",
+                      arg_prefix.c_str (),result.match_list[0]);
+
+  {
+    ui_out_emit_list completions_emitter (uiout, "matches");
+
+    if (result.number_matches == 1)
+      uiout->field_fmt (NULL, "%s%s",
+                        arg_prefix.c_str (), result.match_list[0]);
+    else
+      {
+        result.sort_match_list ();
+        for (size_t i = 0; i < result.number_matches; i++)
+          {
+            uiout->field_fmt (NULL, "%s%s",
+                              arg_prefix.c_str (), result.match_list[i + 1]);
+          }
+      }
+  }
+  uiout->field_string ("max_completions_reached",
+                       result.number_matches == max_completions ? "1" : "0");
+}
+
+
 void
 _initialize_mi_main (void)
 {
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index d46422635b..eed638c5b2 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,3 +1,8 @@
+2019-01-28  Jan Vrany  <jan.vrany@fit.cvut.cz>
+
+	* gdb.mi/mi-complete.exp: New file.
+	* gdb.mi/mi-complete.cc: Likewise.
+
 2019-04-15  Leszek Swirski  <leszeks@google.com>
 
 	* gdb.arch/amd64-eval.cc: New file.
diff --git a/gdb/testsuite/gdb.mi/mi-complete.cc b/gdb/testsuite/gdb.mi/mi-complete.cc
new file mode 100644
index 0000000000..3742152864
--- /dev/null
+++ b/gdb/testsuite/gdb.mi/mi-complete.cc
@@ -0,0 +1,40 @@
+/* Copyright 2018-2019 Free Software Foundation, Inc.
+
+   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/>.  */
+
+#include <vector>
+
+class A
+{
+public:
+  void push_back (void *value);
+};
+
+void
+A::push_back (void *value)
+{
+  /* nothing */
+}
+
+int
+main (int argc, char **argv)
+{
+  std::vector < int >v;
+  v.push_back (1);
+  A a;
+  a.push_back (&v);
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.mi/mi-complete.exp b/gdb/testsuite/gdb.mi/mi-complete.exp
new file mode 100644
index 0000000000..692b004fd9
--- /dev/null
+++ b/gdb/testsuite/gdb.mi/mi-complete.exp
@@ -0,0 +1,72 @@
+# Copyright 2018-2019 Free Software Foundation, Inc.
+
+# 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/>.
+
+# Verify GDB/MI -complete in various scenarios. This test only tests
+# -complete command, not the correctness of completions.
+
+load_lib mi-support.exp
+set MIFLAGS "-i=mi"
+
+gdb_exit
+if [mi_gdb_start] {
+    continue
+}
+
+standard_testfile .cc
+
+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug c++}] != "" } {
+     untested "failed to compile"
+     return -1
+}
+
+mi_run_to_main
+
+mi_gdb_test "1-complete br" \
+            "1\\^done,completion=\"break\",matches=\\\[.*\"break\",.*\"break-range\".*\\\],max_completions_reached=\"0\"" \
+            "-complete br"
+
+# Check empty completion list.
+mi_gdb_test "5-complete bogus" \
+            "5\\^done,matches=\\\[\\\],max_completions_reached=\"0\"" \
+            "-complete bogus"
+
+# Check completions for commands with space.
+mi_gdb_test "4-complete \"b mai\"" \
+            "4\\^done,completion=\"b main\",matches=\\\[.*\"b main\".*\\\],max_completions_reached=\"0\"" \
+            "-complete \"b mai\""
+
+# Check wildmatching.
+mi_gdb_test "5-complete \"b push_ba\"" \
+            "5\\^done,completion=\"b push_back\\(\",matches=\\\[.*\"b A::push_back\\(void\\*\\)\".*\\\],max_completions_reached=\"0\"" \
+            "-complete \"b push_ba\", wildmatching"
+
+mi_gdb_test "-info-gdb-mi-command complete" \
+            "\\^done,command=\{exists=\"true\"\}" \
+            "-info-gdb-mi-command complete"
+
+# Limit max completions and check that max_completions_reached=\"0\" is set
+# to 1.
+send_gdb "set max-completions 1\n"
+
+mi_gdb_test "2-complete br" \
+            ".*2\\^done,completion=\"br\[A-Za-z0-9-\]+\",matches=\\\[\"br\[A-Za-z0-9-\]+\"\\\],max_completions_reached=\"1\"" \
+            "-complete br, max-completions 1"
+
+# Disable completions and check an error is returned
+send_gdb "set max-completions 0\n"
+
+mi_gdb_test "3-complete br" \
+            ".*3\\^error,msg=\".*" \
+            "-complete br, max-completions 0"
-- 
2.20.1

^ permalink raw reply	[flat|nested] 50+ messages in thread

* [PATCH v4 0/2] MI: Add new command -complete
  2019-01-28 12:41   ` [PATCH v2 0/2] " Jan Vrany
                       ` (8 preceding siblings ...)
  2019-04-18 11:59     ` [PATCH v4 2/2] MI: Add new command -complete Jan Vrany
@ 2019-04-18 11:59     ` Jan Vrany
  2019-04-18 14:59       ` [PATCH v5 2/2] " Jan Vrany
                         ` (2 more replies)
  2019-05-30 13:49     ` [PATCH v3 0/5] Create MI commands using python Jan Vrany
                       ` (5 subsequent siblings)
  15 siblings, 3 replies; 50+ messages in thread
From: Jan Vrany @ 2019-04-18 11:59 UTC (permalink / raw)
  To: gdb-patches; +Cc: Jan Vrany

This is a rework of v3 patch based on further Pedro's comments
and on Eli's comments

Differences v3 -> v4:

  * fixed "few nits" based on Petro's comments
  * fixed/improved documentation based on Eli's comments

Differences v2 -> v3:

  * to support wildmatching, the command output had to change to
    provide both, "completion" (what a frontend should complete)
    and "matches" (a list of possible completions)
  * and some formatting issues.

Differences v1 -> v2:

  * extracted common completion logic to a new helper function
  * implemented MI command using a new mi-specific function rather
    than using CLI implementation.


Jan Vrany (2):
  MI: extract command completion logic from complete_command()
  MI: Add new command -complete

 gdb/ChangeLog                        | 14 ++++++
 gdb/NEWS                             |  7 +++
 gdb/cli/cli-cmds.c                   | 29 +----------
 gdb/completer.c                      | 35 ++++++++++++++
 gdb/completer.h                      |  7 +++
 gdb/doc/ChangeLog                    |  5 ++
 gdb/doc/gdb.texinfo                  | 61 +++++++++++++++++++++++
 gdb/mi/mi-cmds.c                     |  1 +
 gdb/mi/mi-cmds.h                     |  1 +
 gdb/mi/mi-main.c                     | 45 +++++++++++++++++
 gdb/testsuite/ChangeLog              |  5 ++
 gdb/testsuite/gdb.mi/mi-complete.cc  | 40 ++++++++++++++++
 gdb/testsuite/gdb.mi/mi-complete.exp | 72 ++++++++++++++++++++++++++++
 13 files changed, 294 insertions(+), 28 deletions(-)
 create mode 100644 gdb/testsuite/gdb.mi/mi-complete.cc
 create mode 100644 gdb/testsuite/gdb.mi/mi-complete.exp

-- 
2.20.1

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH v4 2/2] MI: Add new command -complete
  2019-04-18 11:59     ` [PATCH v4 2/2] MI: Add new command -complete Jan Vrany
@ 2019-04-18 12:51       ` Eli Zaretskii
  2019-04-18 14:15       ` Pedro Alves
  1 sibling, 0 replies; 50+ messages in thread
From: Eli Zaretskii @ 2019-04-18 12:51 UTC (permalink / raw)
  To: Jan Vrany; +Cc: gdb-patches

> From: Jan Vrany <jan.vrany@fit.cvut.cz>
> Cc: Jan Vrany <jan.vrany@fit.cvut.cz>
> Date: Thu, 18 Apr 2019 12:58:47 +0100
> 
> +The result consists of two one or two fields:
                          ^^^^^^^^^^^^^^
A typo.

> +@smallexample
> +(gdb)
> +-complete br
> +^done,completion="break",matches=["break","break-range"],max_completions_reached="0"
> +(gdb)
> +-complete "b ma"
> +^done,completion="b ma",matches=["b madvise","b main"],max_completions_reached="0"
> +(gdb)
> +-complete "b push_b"
> +^done,completion="b push_back(",
> +      matches=["b A::push_back(void*)",
> +               "b std::string::push_back(char)",
> +               "b std::vector<int, std::allocator<int> >::push_back(int&&)"],
> +      max_completions_reached="0"

The "done" lines are too long, please break them into lines shorter
than 70 characters.

Otherwise, the documentation parts are OK.

Thanks.

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH v4 2/2] MI: Add new command -complete
  2019-04-18 11:59     ` [PATCH v4 2/2] MI: Add new command -complete Jan Vrany
  2019-04-18 12:51       ` Eli Zaretskii
@ 2019-04-18 14:15       ` Pedro Alves
  2019-04-18 14:55         ` Jan Vrany
  1 sibling, 1 reply; 50+ messages in thread
From: Pedro Alves @ 2019-04-18 14:15 UTC (permalink / raw)
  To: Jan Vrany, gdb-patches

This version is OK, with the tiny issue below addressed.

Can you push this yourself, or do you need someone to do it for you?

> +/* Implement the "-complete" command.  */
> +
> +void
> +mi_cmd_complete (const char *command, char **argv, int argc)
> +{
> +  if (argc != 1)
> +    error (_("Usage: -complete COMMAND"));
> +
> +  if (max_completions == 0)
> +    error (_("max-completions is zero, completion is disabled.\n"));

errors should not end in \n.

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH v4 2/2] MI: Add new command -complete
  2019-04-18 14:15       ` Pedro Alves
@ 2019-04-18 14:55         ` Jan Vrany
  2019-04-18 16:14           ` Pedro Alves
  2019-05-16 11:27           ` Jan Vrany
  0 siblings, 2 replies; 50+ messages in thread
From: Jan Vrany @ 2019-04-18 14:55 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

On Thu, 2019-04-18 at 15:14 +0100, Pedro Alves wrote:
> This version is OK, with the tiny issue below addressed.
> 
> Can you push this yourself, or do you need someone to do it for you?

No, I can't. I'll send a v5 with two more fixes shortly - if all is OK, 
can you push it, please? 

Thanks! Jan

> 
> > +/* Implement the "-complete" command.  */
> > +
> > +void
> > +mi_cmd_complete (const char *command, char **argv, int argc)
> > +{
> > +  if (argc != 1)
> > +    error (_("Usage: -complete COMMAND"));
> > +
> > +  if (max_completions == 0)
> > +    error (_("max-completions is zero, completion is disabled.\n"));
> 
> errors should not end in \n.
> 
> Thanks,
> Pedro Alves

^ permalink raw reply	[flat|nested] 50+ messages in thread

* [PATCH v5 1/2] MI: extract command completion logic from complete_command()
  2019-04-18 11:59     ` [PATCH v4 0/2] " Jan Vrany
  2019-04-18 14:59       ` [PATCH v5 2/2] " Jan Vrany
  2019-04-18 14:59       ` [PATCH v5 0/2] " Jan Vrany
@ 2019-04-18 14:59       ` Jan Vrany
  2 siblings, 0 replies; 50+ messages in thread
From: Jan Vrany @ 2019-04-18 14:59 UTC (permalink / raw)
  To: gdb-patches; +Cc: Jan Vrany

Extract completion logic from CLI complete_command() into a new
helper function complete().

gdb/Changelog:

	* completer.h (complete): New function.
	* completer.c (complete): Likewise.
	* cli/cli-cmds.c: (complete_command): Update to use new complete()
	function defined in completer.h.
---
 gdb/ChangeLog      |  7 +++++++
 gdb/cli/cli-cmds.c | 29 +----------------------------
 gdb/completer.c    | 35 +++++++++++++++++++++++++++++++++++
 gdb/completer.h    |  7 +++++++
 4 files changed, 50 insertions(+), 28 deletions(-)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index ef77fdbb5c..f2b0c16ec5 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,10 @@
+2019-01-24  Jan Vrany  <jan.vrany@fit.cvut.cz>
+
+	* completer.h (complete): New function.
+	* completer.c (complete): Likewise.
+	* cli/cli-cmds.c: (complete_command): Update to use new complete()
+	function defined in completer.h.
+
 2019-04-17  Tom Tromey  <tromey@adacore.com>
 
 	* dwarf2read.c (dwarf2_init_complex_target_type): Check "tt"
diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index 8480ba355c..aa9905308c 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -243,40 +243,13 @@ complete_command (const char *arg, int from_tty)
   if (arg == NULL)
     arg = "";
 
-  completion_tracker tracker_handle_brkchars;
-  completion_tracker tracker_handle_completions;
-  completion_tracker *tracker;
-
   int quote_char = '\0';
   const char *word;
 
-  try
-    {
-      word = completion_find_completion_word (tracker_handle_brkchars,
-					      arg, &quote_char);
-
-      /* Completers that provide a custom word point in the
-	 handle_brkchars phase also compute their completions then.
-	 Completers that leave the completion word handling to readline
-	 must be called twice.  */
-      if (tracker_handle_brkchars.use_custom_word_point ())
-	tracker = &tracker_handle_brkchars;
-      else
-	{
-	  complete_line (tracker_handle_completions, word, arg, strlen (arg));
-	  tracker = &tracker_handle_completions;
-	}
-    }
-  catch (const gdb_exception &ex)
-    {
-      return;
-    }
+  completion_result result = complete (arg, &word, &quote_char);
 
   std::string arg_prefix (arg, word - arg);
 
-  completion_result result
-    = tracker->build_completion_result (word, word - arg, strlen (arg));
-
   if (result.number_matches != 0)
     {
       if (result.number_matches == 1)
diff --git a/gdb/completer.c b/gdb/completer.c
index 5d1decca1d..cc2f80bc66 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -1613,6 +1613,41 @@ make_completion_match_str (gdb::unique_xmalloc_ptr<char> &&match_name,
   return gdb::unique_xmalloc_ptr<char> (newobj);
 }
 
+/* See complete.h.  */
+
+completion_result
+complete (const char *line, char const **word, int *quote_char)
+{
+  completion_tracker tracker_handle_brkchars;
+  completion_tracker tracker_handle_completions;
+  completion_tracker *tracker;
+
+  try
+    {
+      *word = completion_find_completion_word (tracker_handle_brkchars,
+					      line, quote_char);
+
+      /* Completers that provide a custom word point in the
+	 handle_brkchars phase also compute their completions then.
+	 Completers that leave the completion word handling to readline
+	 must be called twice.  */
+      if (tracker_handle_brkchars.use_custom_word_point ())
+	tracker = &tracker_handle_brkchars;
+      else
+	{
+	  complete_line (tracker_handle_completions, *word, line, strlen (line));
+	  tracker = &tracker_handle_completions;
+	}
+    }
+  catch (const gdb_exception &ex)
+    {
+      return {};
+    }
+
+  return tracker->build_completion_result (*word, *word - line, strlen (line));
+}
+
+
 /* Generate completions all at once.  Does nothing if max_completions
    is 0.  If max_completions is non-negative, this will collect at
    most max_completions strings.
diff --git a/gdb/completer.h b/gdb/completer.h
index 52f8d7dff4..38a0132ca4 100644
--- a/gdb/completer.h
+++ b/gdb/completer.h
@@ -510,6 +510,13 @@ extern void complete_line (completion_tracker &tracker,
 			   const char *line_buffer,
 			   int point);
 
+/* Complete LINE and return completion results.  For completion purposes,
+   cursor position is assumed to be at the end of LINE.  WORD is set to
+   the end of word to complete.  QUOTE_CHAR is set to the opening quote
+   character if we found an unclosed quoted substring, '\0' otherwise.  */
+extern completion_result
+  complete (const char *line, char const **word, int *quote_char);
+
 /* Find the bounds of the word in TEXT for completion purposes, and
    return a pointer to the end of the word.  Calls the completion
    machinery for a handle_brkchars phase (using TRACKER) to figure out
-- 
2.20.1

^ permalink raw reply	[flat|nested] 50+ messages in thread

* [PATCH v5 0/2] MI: Add new command -complete
  2019-04-18 11:59     ` [PATCH v4 0/2] " Jan Vrany
  2019-04-18 14:59       ` [PATCH v5 2/2] " Jan Vrany
@ 2019-04-18 14:59       ` Jan Vrany
  2019-04-18 14:59       ` [PATCH v5 1/2] MI: extract command completion logic from complete_command() Jan Vrany
  2 siblings, 0 replies; 50+ messages in thread
From: Jan Vrany @ 2019-04-18 14:59 UTC (permalink / raw)
  To: gdb-patches; +Cc: Jan Vrany

This is a rework of v4 patch based on further Pedro's comments
and on Eli's comments

Differences v4 -> v5:

  * removed "\n" from error message
  * fixed typo and long lines in documentation

Differences v3 -> v4:

  * fixed "few nits" based on Petro's comments
  * fixed/improved documentation based on Eli's comments

Differences v2 -> v3:

  * to support wildmatching, the command output had to change to
    provide both, "completion" (what a frontend should complete)
    and "matches" (a list of possible completions)
  * and some formatting issues.

Differences v1 -> v2:

  * extracted common completion logic to a new helper function
  * implemented MI command using a new mi-specific function rather
    than using CLI implementation.


Jan Vrany (2):
  MI: extract command completion logic from complete_command()
  MI: Add new command -complete

 gdb/ChangeLog                        | 14 ++++++
 gdb/NEWS                             |  7 +++
 gdb/cli/cli-cmds.c                   | 29 +----------
 gdb/completer.c                      | 35 ++++++++++++++
 gdb/completer.h                      |  7 +++
 gdb/doc/ChangeLog                    |  5 ++
 gdb/doc/gdb.texinfo                  | 65 +++++++++++++++++++++++++
 gdb/mi/mi-cmds.c                     |  1 +
 gdb/mi/mi-cmds.h                     |  1 +
 gdb/mi/mi-main.c                     | 45 +++++++++++++++++
 gdb/testsuite/ChangeLog              |  5 ++
 gdb/testsuite/gdb.mi/mi-complete.cc  | 40 ++++++++++++++++
 gdb/testsuite/gdb.mi/mi-complete.exp | 72 ++++++++++++++++++++++++++++
 13 files changed, 298 insertions(+), 28 deletions(-)
 create mode 100644 gdb/testsuite/gdb.mi/mi-complete.cc
 create mode 100644 gdb/testsuite/gdb.mi/mi-complete.exp

-- 
2.20.1

^ permalink raw reply	[flat|nested] 50+ messages in thread

* [PATCH v5 2/2] MI: Add new command -complete
  2019-04-18 11:59     ` [PATCH v4 0/2] " Jan Vrany
@ 2019-04-18 14:59       ` Jan Vrany
  2019-04-18 15:23         ` Eli Zaretskii
  2019-04-18 14:59       ` [PATCH v5 0/2] " Jan Vrany
  2019-04-18 14:59       ` [PATCH v5 1/2] MI: extract command completion logic from complete_command() Jan Vrany
  2 siblings, 1 reply; 50+ messages in thread
From: Jan Vrany @ 2019-04-18 14:59 UTC (permalink / raw)
  To: gdb-patches; +Cc: Jan Vrany

There is a CLI command 'complete' intended to use with emacs. Such a command
would also be useful for MI frontends, when separate CLI and MI channels cannot
be used. For example, on Windows (because of lack of PTYs) or when GDB is used
through SSH session.

This commit adds a new '-complete' MI command.

gdb/Changelog:
2019-01-28  Jan Vrany  <jan.vrany@fit.cvut.cz>

	* mi/mi-cmds.h (mi_cmd_complete): New function.
	* mi/mi-main.c (mi_cmd_complete): Likewise.
	* mi/mi-cmds.c: Define new MI command -complete.
	* NEWS: Mention new -complete command.

gdb/doc/ChangeLog:
2019-01-28  Jan Vrany  <jan.vrany@fit.cvut.cz>

	* gdb.texinfo (Miscellaneous GDB/MI Commands): Document new
	MI command -complete.

gdb/testsuite/ChangeLog:
2019-01-28  Jan Vrany  <jan.vrany@fit.cvut.cz>

	* gdb.mi/mi-complete.exp: New file.
	* gdb.mi/mi-complete.cc: Likewise.
---
 gdb/ChangeLog                        |  7 +++
 gdb/NEWS                             |  7 +++
 gdb/doc/ChangeLog                    |  5 ++
 gdb/doc/gdb.texinfo                  | 65 +++++++++++++++++++++++++
 gdb/mi/mi-cmds.c                     |  1 +
 gdb/mi/mi-cmds.h                     |  1 +
 gdb/mi/mi-main.c                     | 45 +++++++++++++++++
 gdb/testsuite/ChangeLog              |  5 ++
 gdb/testsuite/gdb.mi/mi-complete.cc  | 40 ++++++++++++++++
 gdb/testsuite/gdb.mi/mi-complete.exp | 72 ++++++++++++++++++++++++++++
 10 files changed, 248 insertions(+)
 create mode 100644 gdb/testsuite/gdb.mi/mi-complete.cc
 create mode 100644 gdb/testsuite/gdb.mi/mi-complete.exp

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index f2b0c16ec5..87fa43dd06 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,10 @@
+2019-01-28  Jan Vrany  <jan.vrany@fit.cvut.cz>
+
+	* mi/mi-cmds.h (mi_cmd_complete): New function.
+	* mi/mi-main.c (mi_cmd_complete): Likewise.
+	* mi/mi-cmds.c: Define new MI command -complete.
+	* NEWS: Mention new -complete command.
+
 2019-01-24  Jan Vrany  <jan.vrany@fit.cvut.cz>
 
 	* completer.h (complete): New function.
diff --git a/gdb/NEWS b/gdb/NEWS
index 5309a8f923..7aa31ccb17 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -27,6 +27,13 @@
      'array_indexes', 'symbols', 'unions', 'deref_refs', 'actual_objects',
      'static_members', 'max_elements', 'repeat_threshold', and 'format'.
 
+* New MI commands
+
+-complete
+  This lists all the possible completions for the rest of the line, if it
+  were to be given as a command itself.  This is intended for use by MI 
+  frontends in cases when separate CLI and MI channels cannot be used.
+
 *** Changes in GDB 8.3
 
 * GDB and GDBserver now support access to additional registers on
diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog
index 64073278fa..6f7a48787e 100644
--- a/gdb/doc/ChangeLog
+++ b/gdb/doc/ChangeLog
@@ -1,3 +1,8 @@
+2019-01-28  Jan Vrany  <jan.vrany@fit.cvut.cz>
+
+	* gdb.texinfo (Miscellaneous GDB/MI Commands): Document new
+	MI command -complete.
+
 2019-04-17  Alan Hayward  <alan.hayward@arm.com>
 
 	* gdb.texinfo (Other Command-Line Arguments for gdbserver)
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index a3a5f3e28c..ca67902148 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -34438,6 +34438,71 @@ fullname="/home/nickrob/myprog.c",line="73",arch="i386:x86_64"@}
 (gdb)
 @end smallexample
 
+@subheading The @code{-complete} Command
+@findex -complete
+
+@subheading Synopsis
+
+@smallexample
+-complete @var{command}
+@end smallexample
+
+Show a list of completions for partially typed CLI @var{command}.
+
+This command is intended for @sc{gdb/mi} frontends that cannot use two separate
+CLI and MI channels - for example: because of lack of PTYs like on Windows or
+because @value{GDBN} is used remotely via a SSH connection.
+
+@subheading Result
+
+The result consists of two or three fields:
+
+@table @samp
+@item completion
+This field contains the completed @var{command}.  If @var{command}
+has no known completions, this field is omitted.
+
+@item matches
+This field contains a (possibly empty) array of matches.  It is always present.
+
+@item max_completions_reached
+This field contains @code{1} if number of known completions is above
+@code{max-completions} limit (see @ref{Completion}), otherwise it contains
+@code{0}.  It is always present.
+
+@end table
+
+@subheading @value{GDBN} Command
+
+The corresponding @value{GDBN} command is @samp{complete}.
+
+@subheading Example
+
+@smallexample
+(gdb)
+-complete br
+^done,completion="break",
+      matches=["break","break-range"],
+      max_completions_reached="0"
+(gdb)
+-complete "b ma"
+^done,completion="b ma",
+      matches=["b madvise","b main"],max_completions_reached="0"
+(gdb)
+-complete "b push_b"
+^done,completion="b push_back(",
+      matches=[
+       "b A::push_back(void*)",
+       "b std::string::push_back(char)",
+       "b std::vector<int, std::allocator<int> >::push_back(int&&)"],
+      max_completions_reached="0"
+(gdb)
+-complete "nonexist"
+^done,matches=[],max_completions_reached="0"
+(gdb)
+
+@end smallexample
+
 @node Annotations
 @chapter @value{GDBN} Annotations
 
diff --git a/gdb/mi/mi-cmds.c b/gdb/mi/mi-cmds.c
index fe30ac2e82..bbc0e2bd93 100644
--- a/gdb/mi/mi-cmds.c
+++ b/gdb/mi/mi-cmds.c
@@ -75,6 +75,7 @@ static struct mi_cmd mi_cmds[] =
                    &mi_suppress_notification.breakpoint),
   DEF_MI_CMD_MI_1 ("catch-unload", mi_cmd_catch_unload,
                    &mi_suppress_notification.breakpoint),
+  DEF_MI_CMD_MI ("complete", mi_cmd_complete),
   DEF_MI_CMD_MI ("data-disassemble", mi_cmd_disassemble),
   DEF_MI_CMD_MI ("data-evaluate-expression", mi_cmd_data_evaluate_expression),
   DEF_MI_CMD_MI ("data-list-changed-registers",
diff --git a/gdb/mi/mi-cmds.h b/gdb/mi/mi-cmds.h
index 7b22ce7813..58aa2d6f77 100644
--- a/gdb/mi/mi-cmds.h
+++ b/gdb/mi/mi-cmds.h
@@ -125,6 +125,7 @@ extern mi_cmd_argv_ftype mi_cmd_var_update;
 extern mi_cmd_argv_ftype mi_cmd_enable_pretty_printing;
 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.  */
 
diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c
index 17ca807471..e3e5319f64 100644
--- a/gdb/mi/mi-main.c
+++ b/gdb/mi/mi-main.c
@@ -2726,6 +2726,51 @@ mi_multi_location_breakpoint_output_fixed (ui_out *uiout)
   return mi_uiout->version () >= 3 || fix_multi_location_breakpoint_output;
 }
 
+/* Implement the "-complete" command.  */
+
+void
+mi_cmd_complete (const char *command, char **argv, int argc)
+{
+  if (argc != 1)
+    error (_("Usage: -complete COMMAND"));
+
+  if (max_completions == 0)
+    error (_("max-completions is zero, completion is disabled."));
+
+  int quote_char = '\0';
+  const char *word;
+
+  completion_result result = complete (argv[0], &word, &quote_char);
+
+  std::string arg_prefix (argv[0], word - argv[0]);
+
+  struct ui_out *uiout = current_uiout;
+
+  if (result.number_matches > 0)
+    uiout->field_fmt ("completion", "%s%s",
+                      arg_prefix.c_str (),result.match_list[0]);
+
+  {
+    ui_out_emit_list completions_emitter (uiout, "matches");
+
+    if (result.number_matches == 1)
+      uiout->field_fmt (NULL, "%s%s",
+                        arg_prefix.c_str (), result.match_list[0]);
+    else
+      {
+        result.sort_match_list ();
+        for (size_t i = 0; i < result.number_matches; i++)
+          {
+            uiout->field_fmt (NULL, "%s%s",
+                              arg_prefix.c_str (), result.match_list[i + 1]);
+          }
+      }
+  }
+  uiout->field_string ("max_completions_reached",
+                       result.number_matches == max_completions ? "1" : "0");
+}
+
+
 void
 _initialize_mi_main (void)
 {
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index d46422635b..eed638c5b2 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,3 +1,8 @@
+2019-01-28  Jan Vrany  <jan.vrany@fit.cvut.cz>
+
+	* gdb.mi/mi-complete.exp: New file.
+	* gdb.mi/mi-complete.cc: Likewise.
+
 2019-04-15  Leszek Swirski  <leszeks@google.com>
 
 	* gdb.arch/amd64-eval.cc: New file.
diff --git a/gdb/testsuite/gdb.mi/mi-complete.cc b/gdb/testsuite/gdb.mi/mi-complete.cc
new file mode 100644
index 0000000000..3742152864
--- /dev/null
+++ b/gdb/testsuite/gdb.mi/mi-complete.cc
@@ -0,0 +1,40 @@
+/* Copyright 2018-2019 Free Software Foundation, Inc.
+
+   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/>.  */
+
+#include <vector>
+
+class A
+{
+public:
+  void push_back (void *value);
+};
+
+void
+A::push_back (void *value)
+{
+  /* nothing */
+}
+
+int
+main (int argc, char **argv)
+{
+  std::vector < int >v;
+  v.push_back (1);
+  A a;
+  a.push_back (&v);
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.mi/mi-complete.exp b/gdb/testsuite/gdb.mi/mi-complete.exp
new file mode 100644
index 0000000000..692b004fd9
--- /dev/null
+++ b/gdb/testsuite/gdb.mi/mi-complete.exp
@@ -0,0 +1,72 @@
+# Copyright 2018-2019 Free Software Foundation, Inc.
+
+# 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/>.
+
+# Verify GDB/MI -complete in various scenarios. This test only tests
+# -complete command, not the correctness of completions.
+
+load_lib mi-support.exp
+set MIFLAGS "-i=mi"
+
+gdb_exit
+if [mi_gdb_start] {
+    continue
+}
+
+standard_testfile .cc
+
+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug c++}] != "" } {
+     untested "failed to compile"
+     return -1
+}
+
+mi_run_to_main
+
+mi_gdb_test "1-complete br" \
+            "1\\^done,completion=\"break\",matches=\\\[.*\"break\",.*\"break-range\".*\\\],max_completions_reached=\"0\"" \
+            "-complete br"
+
+# Check empty completion list.
+mi_gdb_test "5-complete bogus" \
+            "5\\^done,matches=\\\[\\\],max_completions_reached=\"0\"" \
+            "-complete bogus"
+
+# Check completions for commands with space.
+mi_gdb_test "4-complete \"b mai\"" \
+            "4\\^done,completion=\"b main\",matches=\\\[.*\"b main\".*\\\],max_completions_reached=\"0\"" \
+            "-complete \"b mai\""
+
+# Check wildmatching.
+mi_gdb_test "5-complete \"b push_ba\"" \
+            "5\\^done,completion=\"b push_back\\(\",matches=\\\[.*\"b A::push_back\\(void\\*\\)\".*\\\],max_completions_reached=\"0\"" \
+            "-complete \"b push_ba\", wildmatching"
+
+mi_gdb_test "-info-gdb-mi-command complete" \
+            "\\^done,command=\{exists=\"true\"\}" \
+            "-info-gdb-mi-command complete"
+
+# Limit max completions and check that max_completions_reached=\"0\" is set
+# to 1.
+send_gdb "set max-completions 1\n"
+
+mi_gdb_test "2-complete br" \
+            ".*2\\^done,completion=\"br\[A-Za-z0-9-\]+\",matches=\\\[\"br\[A-Za-z0-9-\]+\"\\\],max_completions_reached=\"1\"" \
+            "-complete br, max-completions 1"
+
+# Disable completions and check an error is returned
+send_gdb "set max-completions 0\n"
+
+mi_gdb_test "3-complete br" \
+            ".*3\\^error,msg=\".*" \
+            "-complete br, max-completions 0"
-- 
2.20.1

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH v5 2/2] MI: Add new command -complete
  2019-04-18 14:59       ` [PATCH v5 2/2] " Jan Vrany
@ 2019-04-18 15:23         ` Eli Zaretskii
  0 siblings, 0 replies; 50+ messages in thread
From: Eli Zaretskii @ 2019-04-18 15:23 UTC (permalink / raw)
  To: Jan Vrany; +Cc: gdb-patches

> From: Jan Vrany <jan.vrany@fit.cvut.cz>
> Cc: Jan Vrany <jan.vrany@fit.cvut.cz>
> Date: Thu, 18 Apr 2019 15:59:13 +0100
> 
> This commit adds a new '-complete' MI command.
> 
> gdb/Changelog:
> 2019-01-28  Jan Vrany  <jan.vrany@fit.cvut.cz>
> 
> 	* mi/mi-cmds.h (mi_cmd_complete): New function.
> 	* mi/mi-main.c (mi_cmd_complete): Likewise.
> 	* mi/mi-cmds.c: Define new MI command -complete.
> 	* NEWS: Mention new -complete command.
> 
> gdb/doc/ChangeLog:
> 2019-01-28  Jan Vrany  <jan.vrany@fit.cvut.cz>
> 
> 	* gdb.texinfo (Miscellaneous GDB/MI Commands): Document new
> 	MI command -complete.
> 
> gdb/testsuite/ChangeLog:
> 2019-01-28  Jan Vrany  <jan.vrany@fit.cvut.cz>
> 
> 	* gdb.mi/mi-complete.exp: New file.
> 	* gdb.mi/mi-complete.cc: Likewise.

Thanks, the documentation parts are OK.

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH v4 2/2] MI: Add new command -complete
  2019-04-18 14:55         ` Jan Vrany
@ 2019-04-18 16:14           ` Pedro Alves
  2019-05-16 11:27           ` Jan Vrany
  1 sibling, 0 replies; 50+ messages in thread
From: Pedro Alves @ 2019-04-18 16:14 UTC (permalink / raw)
  To: Jan Vrany, gdb-patches

On 4/18/19 3:54 PM, Jan Vrany wrote:
> On Thu, 2019-04-18 at 15:14 +0100, Pedro Alves wrote:
>> This version is OK, with the tiny issue below addressed.
>>
>> Can you push this yourself, or do you need someone to do it for you?
> 
> No, I can't. I'll send a v5 with two more fixes shortly - if all is OK, 
> can you push it, please? 

I see you've already posted some more patches.  Do you plan to continue
contributing to GDB?  If so, might as well give you write access to
the repo so you can push your own patches yourself.  Let me know what
you prefer.

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH v4 2/2] MI: Add new command -complete
  2019-04-18 14:55         ` Jan Vrany
  2019-04-18 16:14           ` Pedro Alves
@ 2019-05-16 11:27           ` Jan Vrany
  2019-05-16 17:31             ` Tom Tromey
  1 sibling, 1 reply; 50+ messages in thread
From: Jan Vrany @ 2019-05-16 11:27 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

Polite ping. 

The v5 - hopefully good - is here: 

https://sourceware.org/ml/gdb-patches/2019-04/msg00323.html

Thanks! Jan

On Thu, 2019-04-18 at 15:54 +0100, Jan Vrany wrote:
> On Thu, 2019-04-18 at 15:14 +0100, Pedro Alves wrote:
> > This version is OK, with the tiny issue below addressed.
> > 
> > Can you push this yourself, or do you need someone to do it for you?
> 
> No, I can't. I'll send a v5 with two more fixes shortly - if all is OK, 
> can you push it, please? 
> 
> Thanks! Jan
> 
> > > +/* Implement the "-complete" command.  */
> > > +
> > > +void
> > > +mi_cmd_complete (const char *command, char **argv, int argc)
> > > +{
> > > +  if (argc != 1)
> > > +    error (_("Usage: -complete COMMAND"));
> > > +
> > > +  if (max_completions == 0)
> > > +    error (_("max-completions is zero, completion is disabled.\n"));
> > 
> > errors should not end in \n.
> > 
> > Thanks,
> > Pedro Alves

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH v4 2/2] MI: Add new command -complete
  2019-05-16 11:27           ` Jan Vrany
@ 2019-05-16 17:31             ` Tom Tromey
  0 siblings, 0 replies; 50+ messages in thread
From: Tom Tromey @ 2019-05-16 17:31 UTC (permalink / raw)
  To: Jan Vrany; +Cc: Pedro Alves, gdb-patches

>>>>> "Jan" == Jan Vrany <jan.vrany@fit.cvut.cz> writes:

Jan> Polite ping. 
Jan> The v5 - hopefully good - is here: 
Jan> https://sourceware.org/ml/gdb-patches/2019-04/msg00323.html

I read through these, and they looked good to me.
Please check them in.  Thanks for doing this.

Tom

^ permalink raw reply	[flat|nested] 50+ messages in thread

* [PATCH v3 3/5] Create MI commands using python.
  2019-01-28 12:41   ` [PATCH v2 0/2] " Jan Vrany
                       ` (13 preceding siblings ...)
  2019-05-30 13:49     ` [PATCH v3 2/5] Use classes to represent MI Command instead of structures Jan Vrany
@ 2019-05-30 13:49     ` Jan Vrany
  2019-06-18 19:43       ` Pedro Alves
  2019-05-30 14:19     ` [PATCH v3 5/5] mi/python: Add tests for python-defined MI commands Jan Vrany
  15 siblings, 1 reply; 50+ messages in thread
From: Jan Vrany @ 2019-05-30 13:49 UTC (permalink / raw)
  To: gdb-patches; +Cc: Didier Nadeau

From: Didier Nadeau <didier.nadeau@gmail.com>

This commit allows an user to create custom MI commands using Python
similarly to what is possible for Python CLI commands.

A new subclass of mi_command is defined for Python MI commands,
mi_command_py. A new file, py-micmd.c contains the logic for Python
MI commands.

gdb/ChangeLog

    * Makefile.in (SUBDIR_PYTHON_SRCS): Add py-micmd.c.
    * mi/mi-cmds.c (insert_mi_cmd_entry): Remove static.
    (mi_cmd_table): Remove static.
    * mi/mi-cmds.h (insert_mi_cmd_entry): New declaration.
    (mi_cmd_table): New declaration.
    * python/py-micmd.c: New file
    (parse_mi_result): New function.
    (micmdpy_init): New function.
    (gdbpy_initialize_micommands): New function.
    (mi_command_py): New class.
    * python/py-micmd.h: New file
    (micmdpy_object): New struct.
    (micmdpy_object): New typedef.
    (mi_command_py): New class.
    * python/python-internal.h
    (gdbpy_initialize_micommands): New declaration.
    * python/python.c
    (_initialize_python): New call to gdbpy_initialize_micommands.
    (finalize_python): Finalize Python MI commands.
---
 gdb/ChangeLog                |  23 +++
 gdb/Makefile.in              |   1 +
 gdb/mi/mi-cmds.c             |   7 +-
 gdb/mi/mi-cmds.h             |   9 ++
 gdb/python/py-micmd.c        | 297 +++++++++++++++++++++++++++++++++++
 gdb/python/py-micmd.h        |  60 +++++++
 gdb/python/python-internal.h |   2 +
 gdb/python/python.c          |  13 +-
 8 files changed, 407 insertions(+), 5 deletions(-)
 create mode 100644 gdb/python/py-micmd.c
 create mode 100644 gdb/python/py-micmd.h

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 994ad947bd..241e5da68f 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,26 @@
+2019-05-02  Didier Nadeau  <didier.nadeau@gmail.com>
+            Jan Vrany  <jan.vrany@fit.cvut.cz>
+
+	* Makefile.in (SUBDIR_PYTHON_SRCS): Add py-micmd.c.
+	* mi/mi-cmds.c (insert_mi_cmd_entry): Remove static.
+	(mi_cmd_table): Remove static.
+	* mi/mi-cmds.h (insert_mi_cmd_entry): New declaration.
+	(mi_cmd_table): New declaration.
+	* python/py-micmd.c: New file
+	(parse_mi_result): New function.
+	(micmdpy_init): New function.
+	(gdbpy_initialize_micommands): New function.
+	(mi_command_py): New class.
+	* python/py-micmd.h: New file
+	(micmdpy_object): New struct.
+	(micmdpy_object): New typedef.
+	(mi_command_py): New class.
+	* python/python-internal.h
+	(gdbpy_initialize_micommands): New declaration.
+	* python/python.c
+	(_initialize_python): New call to gdbpy_initialize_micommands.
+	(finalize_python): Finalize Python MI commands.
+
 2019-05-02  Didier Nadeau  <didier.nadeau@gmail.com>
 	    Jan Vrany <jan.vrany@fit.cvut.cz>
 
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 0f49578360..28866962bc 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -382,6 +382,7 @@ SUBDIR_PYTHON_SRCS = \
 	python/py-instruction.c \
 	python/py-lazy-string.c \
 	python/py-linetable.c \
+	python/py-micmd.c \
 	python/py-newobjfileevent.c \
 	python/py-objfile.c \
 	python/py-param.c \
diff --git a/gdb/mi/mi-cmds.c b/gdb/mi/mi-cmds.c
index 2d8863c5f9..0aead954f9 100644
--- a/gdb/mi/mi-cmds.c
+++ b/gdb/mi/mi-cmds.c
@@ -28,12 +28,11 @@
 
 /* MI command table (built at run time). */
 
-static std::map<std::string, mi_cmd_up> mi_cmd_table;
+std::map<std::string, mi_cmd_up> mi_cmd_table;
 
-/* Insert a new mi-command into the command table.  Return true if
-   insertion was successful.  */
+/* See mi-cmds.h.  */
 
-static bool
+bool
 insert_mi_cmd_entry (mi_cmd_up command)
 {
   gdb_assert (command != NULL);
diff --git a/gdb/mi/mi-cmds.h b/gdb/mi/mi-cmds.h
index f1b4728bde..5ca7232fca 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 <map>
+
 enum print_values {
    PRINT_NO_VALUES,
    PRINT_ALL_VALUES,
@@ -191,9 +193,16 @@ typedef std::unique_ptr<mi_command> mi_cmd_up;
 
 extern mi_command *mi_cmd_lookup (const char *command);
 
+extern std::map<std::string, mi_cmd_up> mi_cmd_table;
+
 /* Debug flag */
 extern int mi_debug_p;
 
 extern void mi_execute_command (const char *cmd, int from_tty);
 
+/* Insert a new mi-command into the command table.  Return true if
+   insertion was successful.  */
+
+extern bool insert_mi_cmd_entry (mi_cmd_up command);
+
 #endif /* MI_MI_CMDS_H */
diff --git a/gdb/python/py-micmd.c b/gdb/python/py-micmd.c
new file mode 100644
index 0000000000..e99632c97a
--- /dev/null
+++ b/gdb/python/py-micmd.c
@@ -0,0 +1,297 @@
+/* MI Command Set for GDB, the GNU debugger.
+
+   Copyright (C) 2019 Free Software Foundation, Inc.
+
+   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/>.  */
+
+/* gdb MI commands implemented in Python  */
+
+#include "defs.h"
+#include "python-internal.h"
+#include "python/py-micmd.h"
+#include "arch-utils.h"
+#include "charset.h"
+#include "language.h"
+
+#include <string>
+
+static PyObject *invoke_cst;
+
+extern PyTypeObject
+  micmdpy_object_type CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("micmdpy_object");
+
+/* If the command invoked returns a list, this function parses it and create an
+   appropriate MI out output.
+
+   The returned values must be Python string, and can be contained within Python
+   lists and dictionaries. It is possible to have a multiple levels of lists
+   and/or dictionaries.  */
+
+static void
+parse_mi_result (PyObject *result, const char *field_name)
+{
+  struct ui_out *uiout = current_uiout;
+
+  if (PyDict_Check (result))
+    {
+      PyObject *key, *value;
+      Py_ssize_t pos = 0;
+      ui_out_emit_tuple tuple_emitter (uiout, field_name);
+      while (PyDict_Next (result, &pos, &key, &value))
+	{
+	  if (!PyString_Check (key))
+	    {
+	      gdbpy_ref<> key_repr (PyObject_Repr (key));
+	      if (PyErr_Occurred () != NULL)
+                {
+                  gdbpy_err_fetch ex;
+                  gdb::unique_xmalloc_ptr<char> ex_msg (ex.to_string ());
+
+                  if (ex_msg == NULL || *ex_msg == '\0')
+                    error (_("Non-string object used as key."));
+                  else
+                    error (_("Non-string object used as key: %s."),
+                           ex_msg.get ());
+                }
+              else
+	        {
+	          auto key_repr_string
+	                 = python_string_to_target_string (key_repr.get ());
+	          error (_("Non-string object used as key: %s."),
+                         key_repr_string.get ());
+	        }
+	    }
+
+	  auto key_string = python_string_to_target_string (key);
+	  parse_mi_result (value, key_string.get ());
+	}
+    }
+  else if (PySequence_Check (result) && !PyString_Check (result))
+    {
+      ui_out_emit_list list_emitter (uiout, field_name);
+      for (Py_ssize_t i = 0; i < PySequence_Size (result); ++i)
+	{
+          gdbpy_ref<> item (PySequence_ITEM (result, i));
+	  parse_mi_result (item.get (), NULL);
+	}
+    }
+  else if (PyIter_Check (result))
+    {
+      gdbpy_ref<> item;
+      ui_out_emit_list list_emitter (uiout, field_name);
+      while (item.reset (PyIter_Next (result)), item != nullptr)
+	parse_mi_result (item.get (), NULL);
+    }
+  else
+    {
+      gdb::unique_xmalloc_ptr<char> string (gdbpy_obj_to_string (result));
+      uiout->field_string (field_name, string.get ());
+    }
+}
+
+/* Object initializer; sets up gdb-side structures for MI command.
+
+   Use: __init__(NAME).
+
+   NAME is the name of the MI command to register.  It must start with a dash
+   as traditional MI commands do.  */
+
+static int
+micmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
+{
+  const char *name;
+  gdbpy_ref<> self_ref = gdbpy_ref<>::new_reference (self);
+
+  if (!PyArg_ParseTuple (args, "s", &name))
+    return -1;
+
+  /* Validate command name */
+  const int name_len = strlen (name);
+  if (name_len == 0)
+    {
+      error (_("MI command name is empty."));
+      return -1;
+    }
+  else if ((name_len < 2) || (name[0] != '-') || !isalnum (name[1]))
+    {
+      error (_("MI command name does not start with '-'"
+               " followed by at least one letter or digit."));
+      return -1;
+    }
+  else
+    for (int i = 2; i < name_len; i++)
+      {
+	if (!isalnum (name[i]) && name[i] != '-')
+	  {
+	    error (_("MI command name contains invalid character: %c."),
+                   name[i]);
+	    return -1;
+	  }
+      }
+
+  if (!PyObject_HasAttr (self_ref.get (), invoke_cst))
+      error (_("-%s: Python command object missing 'invoke' method."), name);
+
+  try
+    {
+      mi_cmd_up micommand = mi_cmd_up(new mi_command_py (name + 1, self_ref));
+
+      bool result = insert_mi_cmd_entry (std::move (micommand));
+
+      if (!result)
+	{
+	  error (_("Unable to insert command."
+                   "The name might already be in use."));
+	  return -1;
+	}
+    }
+  catch (const gdb_exception &except)
+    {
+      GDB_PY_SET_HANDLE_EXCEPTION (except);
+    }
+
+  return 0;
+}
+
+mi_command_py::mi_command_py (const char *name, gdbpy_ref<> object)
+  : mi_command (name, NULL), pyobj (object)
+{
+}
+
+void
+mi_command_py::do_invoke (struct mi_parse *parse)
+{
+  mi_parse_argv (parse->args, parse);
+
+  if (parse->argv == NULL)
+    error (_("Problem parsing arguments: %s %s"), parse->command, parse->args);
+
+  PyObject *obj = this->pyobj.get ();
+
+  gdbpy_enter enter_py (get_current_arch (), current_language);
+
+  gdb_assert (obj != nullptr);
+
+  if (!PyObject_HasAttr (obj, invoke_cst))
+      error (_("-%s: Python command object missing 'invoke' method."),
+	     name ().c_str ());
+
+
+  gdbpy_ref<> argobj (PyList_New (parse->argc));
+  if (argobj == nullptr)
+    {
+      gdbpy_print_stack ();
+      error (_("-%s: failed to create the Python arguments list."),
+	     name ().c_str ());
+    }
+
+  for (int i = 0; i < parse->argc; ++i)
+    {
+      gdbpy_ref<> str (PyUnicode_Decode (parse->argv[i], strlen (parse->argv[i]),
+					 host_charset (), NULL));
+      if (PyList_SetItem (argobj.get (), i, str.release ()) != 0)
+	{
+	  error (_("-%s: failed to create the Python arguments list."),
+		 name ().c_str ());
+	}
+    }
+
+  gdb_assert (PyErr_Occurred () == NULL);
+  gdbpy_ref<> result (
+    PyObject_CallMethodObjArgs (obj, invoke_cst, argobj.get (), NULL));
+  if (PyErr_Occurred () != NULL)
+    {
+      gdbpy_err_fetch ex;
+      gdb::unique_xmalloc_ptr<char> ex_msg (ex.to_string ());
+
+      if (ex_msg == NULL || *ex_msg == '\0')
+	error (_("-%s: failed to execute command"), name ().c_str ());
+      else
+	error (_("-%s: %s"), name ().c_str (), ex_msg.get ());
+    }
+  else
+    {
+      if (Py_None != result)
+	parse_mi_result (result.get (), "result");
+    }
+}
+
+void mi_command_py::finalize ()
+{
+  this->pyobj.reset (nullptr);
+}
+
+/* Initialize the MI command object.  */
+
+int
+gdbpy_initialize_micommands ()
+{
+  micmdpy_object_type.tp_new = PyType_GenericNew;
+  if (PyType_Ready (&micmdpy_object_type) < 0)
+    return -1;
+
+  if (gdb_pymodule_addobject (gdb_module, "MICommand",
+			      (PyObject *) &micmdpy_object_type)
+      < 0)
+    return -1;
+
+  invoke_cst = PyString_FromString ("invoke");
+  if (invoke_cst == NULL)
+    return -1;
+
+  return 0;
+}
+
+static PyMethodDef micmdpy_object_methods[] = {{0}};
+
+PyTypeObject micmdpy_object_type = {
+  PyVarObject_HEAD_INIT (NULL, 0) "gdb.MICommand", /*tp_name */
+  sizeof (micmdpy_object),			   /*tp_basicsize */
+  0,						   /*tp_itemsize */
+  0,						   /*tp_dealloc */
+  0,						   /*tp_print */
+  0,						   /*tp_getattr */
+  0,						   /*tp_setattr */
+  0,						   /*tp_compare */
+  0,						   /*tp_repr */
+  0,						   /*tp_as_number */
+  0,						   /*tp_as_sequence */
+  0,						   /*tp_as_mapping */
+  0,						   /*tp_hash */
+  0,						   /*tp_call */
+  0,						   /*tp_str */
+  0,						   /*tp_getattro */
+  0,						   /*tp_setattro */
+  0,						   /*tp_as_buffer */
+  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,	/*tp_flags */
+  "GDB mi-command object",			   /* tp_doc */
+  0,						   /* tp_traverse */
+  0,						   /* tp_clear */
+  0,						   /* tp_richcompare */
+  0,						   /* tp_weaklistoffset */
+  0,						   /* tp_iter */
+  0,						   /* tp_iternext */
+  micmdpy_object_methods,			   /* tp_methods */
+  0,						   /* tp_members */
+  0,						   /* tp_getset */
+  0,						   /* tp_base */
+  0,						   /* tp_dict */
+  0,						   /* tp_descr_get */
+  0,						   /* tp_descr_set */
+  0,						   /* tp_dictoffset */
+  micmdpy_init,					   /* tp_init */
+  0,						   /* tp_alloc */
+};
diff --git a/gdb/python/py-micmd.h b/gdb/python/py-micmd.h
new file mode 100644
index 0000000000..deadc0116b
--- /dev/null
+++ b/gdb/python/py-micmd.h
@@ -0,0 +1,60 @@
+/* MI Command Set for GDB, the GNU debugger.
+
+   Copyright (C) 2019 Free Software Foundation, Inc.
+
+   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/>.  */
+
+#ifndef PY_MICMDS_H
+#define PY_MICMDS_H
+
+#include "mi/mi-cmds.h"
+#include "mi/mi-parse.h"
+#include "python-internal.h"
+#include "python/py-ref.h"
+
+struct micmdpy_object
+{
+  PyObject_HEAD
+};
+
+typedef struct micmdpy_object micmdpy_object;
+
+/* MI command implemented in Python.  */
+
+class mi_command_py : public mi_command
+{
+  public:
+    /* Constructs a new mi_command_py object.  NAME is command name without
+       leading dash.  OBJECT is a reference to a Python object implementing
+       the command.  This object should inherit from gdb.MICommand and should
+       implement method invoke (args). */
+
+    mi_command_py (const char *name, gdbpy_ref<> object);
+
+
+    /* This is called just before shutting down a Python interpreter
+       to release python object implementing the command. */
+
+    void finalize ();
+
+  protected:
+   virtual void do_invoke(struct mi_parse *parse) override;
+
+  private:
+    gdbpy_ref<> pyobj;
+};
+
+#endif
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 69ff1fe30d..f28027741e 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -539,6 +539,8 @@ int gdbpy_initialize_xmethods (void)
   CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
 int gdbpy_initialize_unwind (void)
   CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
+int gdbpy_initialize_micommands (void)
+  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
 
 /* A wrapper for PyErr_Fetch that handles reference counting for the
    caller.  */
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 4dad8ec10d..901c0a4edc 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -36,6 +36,8 @@
 #include <ctype.h>
 #include "location.h"
 #include "ser-event.h"
+#include "mi/mi-cmds.h"
+#include "py-micmd.h"
 
 /* Declared constants and enum for python stack printing.  */
 static const char python_excp_none[] = "none";
@@ -1564,6 +1566,14 @@ finalize_python (void *ignore)
   python_gdbarch = target_gdbarch ();
   python_language = current_language;
 
+  for (const auto& name_and_cmd : mi_cmd_table)
+    {
+      mi_command *cmd = name_and_cmd.second.get ();
+      mi_command_py *cmd_py = dynamic_cast<mi_command_py*> (cmd);
+      if (cmd_py != nullptr)
+        cmd_py->finalize ();
+    }
+
   Py_Finalize ();
 
   restore_active_ext_lang (previous_active);
@@ -1698,7 +1708,8 @@ do_start_initialization ()
       || gdbpy_initialize_event () < 0
       || gdbpy_initialize_arch () < 0
       || gdbpy_initialize_xmethods () < 0
-      || gdbpy_initialize_unwind () < 0)
+      || gdbpy_initialize_unwind () < 0
+      || gdbpy_initialize_micommands () < 0)
     return false;
 
 #define GDB_PY_DEFINE_EVENT_TYPE(name, py_name, doc, base)	\
-- 
2.20.1

^ permalink raw reply	[flat|nested] 50+ messages in thread

* [PATCH v3 1/5] Use std::map for MI commands in mi-cmds.c
  2019-01-28 12:41   ` [PATCH v2 0/2] " Jan Vrany
                       ` (10 preceding siblings ...)
  2019-05-30 13:49     ` [PATCH v3 0/5] Create MI commands using python Jan Vrany
@ 2019-05-30 13:49     ` Jan Vrany
  2019-05-30 13:49     ` [PATCH v3 4/5] mi/python: Allow redefinition of python MI commands Jan Vrany
                       ` (3 subsequent siblings)
  15 siblings, 0 replies; 50+ messages in thread
From: Jan Vrany @ 2019-05-30 13:49 UTC (permalink / raw)
  To: gdb-patches; +Cc: Didier Nadeau

From: Didier Nadeau <didier.nadeau@gmail.com>

This changes the hashmap used in mi-cmds.c from a custom
structure to std::map. This is done to be able to add new
MI commands at runtime, the previous structure had a fixed
size array.

gdb/ChangeLog:

	* mi/mi-cmds.h (mi_cmd_up): New typedef.
	(mi_lookup): Rename to ...
	(mi_cmd_lookup): this.
	* mi/mi-cmds.c (_initialize_mi_cmds): Remove declaration.
	(lookup_table): Remove declaration.
	(build_table): Remove declaration.
	(mi_table): Rename to ...
	(mi_cmd_table): this and change to std::map.
	(insert_mi_cmd_entry): New function.
	(DEF_MI_CMD_CLI_1, DEF_MI_CMD_CLI, DEF_MI_CMD_MI,
	DEF_MI_CMD_MI_1): Remove macros.
	(mi_cmds): Remove array.
	(mi_lookup): Rename to ...
	(mi_cmd_lookup): this.
	(create_mi_cmd): New function.
	(struct mi_cmd_stats): Remove.
	(stats): Remove.
	(add_mi_cmd_cli, add_mi_cmd_mi): New functions.
	(lookup_table): Remove.
	(build_table): Remove parameter.
	* mi/mi-parse.c (mi_parse): Change call from mi_lookup to
	mi_cmd_lookup.
	* mi/mi-cmd-info.c (mi_cmd_info_gdb_mi_command): Change call
	from mi_lookup to mi_cmd_lookup.
---
 gdb/ChangeLog        |  28 +++
 gdb/mi/mi-cmd-info.c |   2 +-
 gdb/mi/mi-cmds.c     | 463 ++++++++++++++++++++-----------------------
 gdb/mi/mi-cmds.h     |   4 +-
 gdb/mi/mi-parse.c    |   2 +-
 5 files changed, 252 insertions(+), 247 deletions(-)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 87ae372176..8e964c0adb 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,31 @@
+2019-05-02  Didier Nadeau  <didier.nadeau@gmail.com>
+            Jan Vrany <jan.vrany@fit.cvut.cz>
+
+        * mi/mi-cmds.h (mi_cmd_up): New typedef.
+        (mi_lookup): Rename to ...
+        (mi_cmd_lookup): this.
+        * mi/mi-cmds.c (_initialize_mi_cmds): Remove declaration.
+        (lookup_table): Remove declaration.
+        (build_table): Remove declaration.
+        (mi_table): Rename to ...
+        (mi_cmd_table): this and change to std::map.
+        (insert_mi_cmd_entry): New function.
+        (DEF_MI_CMD_CLI_1, DEF_MI_CMD_CLI, DEF_MI_CMD_MI,
+        DEF_MI_CMD_MI_1): Remove macros.
+        (mi_cmds): Remove array.
+        (mi_lookup): Rename to ...
+        (mi_cmd_lookup): this.
+        (create_mi_cmd): New function.
+        (struct mi_cmd_stats): Remove.
+        (stats): Remove.
+        (add_mi_cmd_cli, add_mi_cmd_mi): New functions.
+        (lookup_table): Remove.
+        (build_table): Remove parameter.
+        * mi/mi-parse.c (mi_parse): Change call from mi_lookup to
+        mi_cmd_lookup.
+        * mi/mi-cmd-info.c (mi_cmd_info_gdb_mi_command): Change call
+        from mi_lookup to mi_cmd_lookup.
+
 2019-05-22  Tom Tromey  <tromey@adacore.com>
 
 	* target.c (target_follow_exec): Constify parameter.
diff --git a/gdb/mi/mi-cmd-info.c b/gdb/mi/mi-cmd-info.c
index 39da6c489d..8645fac294 100644
--- a/gdb/mi/mi-cmd-info.c
+++ b/gdb/mi/mi-cmd-info.c
@@ -82,7 +82,7 @@ mi_cmd_info_gdb_mi_command (const char *command, char **argv, int argc)
   if (cmd_name[0] == '-')
     cmd_name++;
 
-  cmd = mi_lookup (cmd_name);
+  cmd = mi_cmd_lookup (cmd_name);
 
   ui_out_emit_tuple tuple_emitter (uiout, "command");
   uiout->field_string ("exists", cmd != NULL ? "true" : "false");
diff --git a/gdb/mi/mi-cmds.c b/gdb/mi/mi-cmds.c
index bbc0e2bd93..13db22afaf 100644
--- a/gdb/mi/mi-cmds.c
+++ b/gdb/mi/mi-cmds.c
@@ -22,268 +22,243 @@
 #include "top.h"
 #include "mi-cmds.h"
 #include "mi-main.h"
+#include <map>
+#include <string>
 
-struct mi_cmd;
-static struct mi_cmd **lookup_table (const char *command);
-static void build_table (struct mi_cmd *commands);
+/* MI command table (built at run time). */
 
-static struct mi_cmd mi_cmds[] =
+static std::map<std::string, mi_cmd_up> mi_cmd_table;
+
+/* Insert a new mi-command into the command table.  Return true if
+   insertion was successful.  */
+
+static bool
+insert_mi_cmd_entry (mi_cmd_up command)
 {
-/* Define a MI command of NAME, and its corresponding CLI command is
-   CLI_NAME.  */
-#define DEF_MI_CMD_CLI_1(NAME, CLI_NAME, ARGS_P, CALLED)	\
-  { NAME, { CLI_NAME, ARGS_P}, NULL, CALLED }
-#define DEF_MI_CMD_CLI(NAME, CLI_NAME, ARGS_P) \
-  DEF_MI_CMD_CLI_1(NAME, CLI_NAME, ARGS_P, NULL)
-
-/* Define a MI command of NAME, and implemented by function MI_FUNC.  */
-#define DEF_MI_CMD_MI_1(NAME, MI_FUNC, CALLED) \
-  { NAME, {NULL, 0}, MI_FUNC, CALLED }
-#define DEF_MI_CMD_MI(NAME, MI_FUNC) DEF_MI_CMD_MI_1(NAME, MI_FUNC, NULL)
-
-  DEF_MI_CMD_MI ("ada-task-info", mi_cmd_ada_task_info),
-  DEF_MI_CMD_MI ("add-inferior", mi_cmd_add_inferior),
-  DEF_MI_CMD_CLI_1 ("break-after", "ignore", 1,
-		    &mi_suppress_notification.breakpoint),
-  DEF_MI_CMD_CLI_1 ("break-condition","cond", 1,
-		  &mi_suppress_notification.breakpoint),
-  DEF_MI_CMD_MI_1 ("break-commands", mi_cmd_break_commands,
-		   &mi_suppress_notification.breakpoint),
-  DEF_MI_CMD_CLI_1 ("break-delete", "delete breakpoint", 1,
-		    &mi_suppress_notification.breakpoint),
-  DEF_MI_CMD_CLI_1 ("break-disable", "disable breakpoint", 1,
-		    &mi_suppress_notification.breakpoint),
-  DEF_MI_CMD_CLI_1 ("break-enable", "enable breakpoint", 1,
-		     &mi_suppress_notification.breakpoint),
-  DEF_MI_CMD_CLI ("break-info", "info break", 1),
-  DEF_MI_CMD_MI_1 ("break-insert", mi_cmd_break_insert,
-		   &mi_suppress_notification.breakpoint),
-  DEF_MI_CMD_MI_1 ("dprintf-insert", mi_cmd_dprintf_insert,
-		   &mi_suppress_notification.breakpoint),
-  DEF_MI_CMD_CLI ("break-list", "info break", 0),
-  DEF_MI_CMD_MI_1 ("break-passcount", mi_cmd_break_passcount,
-		   &mi_suppress_notification.breakpoint),
-  DEF_MI_CMD_MI_1 ("break-watch", mi_cmd_break_watch,
-		   &mi_suppress_notification.breakpoint),
-  DEF_MI_CMD_MI_1 ("catch-assert", mi_cmd_catch_assert,
-                   &mi_suppress_notification.breakpoint),
-  DEF_MI_CMD_MI_1 ("catch-exception", mi_cmd_catch_exception,
-                   &mi_suppress_notification.breakpoint),
-  DEF_MI_CMD_MI_1 ("catch-handlers", mi_cmd_catch_handlers,
-                   &mi_suppress_notification.breakpoint),
-  DEF_MI_CMD_MI_1 ("catch-load", mi_cmd_catch_load,
-                   &mi_suppress_notification.breakpoint),
-  DEF_MI_CMD_MI_1 ("catch-unload", mi_cmd_catch_unload,
-                   &mi_suppress_notification.breakpoint),
-  DEF_MI_CMD_MI ("complete", mi_cmd_complete),
-  DEF_MI_CMD_MI ("data-disassemble", mi_cmd_disassemble),
-  DEF_MI_CMD_MI ("data-evaluate-expression", mi_cmd_data_evaluate_expression),
-  DEF_MI_CMD_MI ("data-list-changed-registers",
-		 mi_cmd_data_list_changed_registers),
-  DEF_MI_CMD_MI ("data-list-register-names", mi_cmd_data_list_register_names),
-  DEF_MI_CMD_MI ("data-list-register-values", mi_cmd_data_list_register_values),
-  DEF_MI_CMD_MI ("data-read-memory", mi_cmd_data_read_memory),
-  DEF_MI_CMD_MI ("data-read-memory-bytes", mi_cmd_data_read_memory_bytes),
-  DEF_MI_CMD_MI_1 ("data-write-memory", mi_cmd_data_write_memory,
-		   &mi_suppress_notification.memory),
-  DEF_MI_CMD_MI_1 ("data-write-memory-bytes", mi_cmd_data_write_memory_bytes,
-		   &mi_suppress_notification.memory),
-  DEF_MI_CMD_MI ("data-write-register-values",
-		 mi_cmd_data_write_register_values),
-  DEF_MI_CMD_MI ("enable-timings", mi_cmd_enable_timings),
-  DEF_MI_CMD_MI ("enable-pretty-printing", mi_cmd_enable_pretty_printing),
-  DEF_MI_CMD_MI ("enable-frame-filters", mi_cmd_enable_frame_filters),
-  DEF_MI_CMD_MI ("environment-cd", mi_cmd_env_cd),
-  DEF_MI_CMD_MI ("environment-directory", mi_cmd_env_dir),
-  DEF_MI_CMD_MI ("environment-path", mi_cmd_env_path),
-  DEF_MI_CMD_MI ("environment-pwd", mi_cmd_env_pwd),
-  DEF_MI_CMD_CLI_1 ("exec-arguments", "set args", 1,
-		    &mi_suppress_notification.cmd_param_changed),
-  DEF_MI_CMD_MI ("exec-continue", mi_cmd_exec_continue),
-  DEF_MI_CMD_MI ("exec-finish", mi_cmd_exec_finish),
-  DEF_MI_CMD_MI ("exec-jump", mi_cmd_exec_jump),
-  DEF_MI_CMD_MI ("exec-interrupt", mi_cmd_exec_interrupt),
-  DEF_MI_CMD_MI ("exec-next", mi_cmd_exec_next),
-  DEF_MI_CMD_MI ("exec-next-instruction", mi_cmd_exec_next_instruction),
-  DEF_MI_CMD_MI ("exec-return", mi_cmd_exec_return),
-  DEF_MI_CMD_MI ("exec-run", mi_cmd_exec_run),
-  DEF_MI_CMD_MI ("exec-step", mi_cmd_exec_step),
-  DEF_MI_CMD_MI ("exec-step-instruction", mi_cmd_exec_step_instruction),
-  DEF_MI_CMD_CLI ("exec-until", "until", 1),
-  DEF_MI_CMD_CLI ("file-exec-and-symbols", "file", 1),
-  DEF_MI_CMD_CLI ("file-exec-file", "exec-file", 1),
-  DEF_MI_CMD_MI ("file-list-exec-source-file",
-		 mi_cmd_file_list_exec_source_file),
-  DEF_MI_CMD_MI ("file-list-exec-source-files",
-		 mi_cmd_file_list_exec_source_files),
-  DEF_MI_CMD_MI ("file-list-shared-libraries",
-		 mi_cmd_file_list_shared_libraries),
-  DEF_MI_CMD_CLI ("file-symbol-file", "symbol-file", 1),
-  DEF_MI_CMD_MI ("fix-multi-location-breakpoint-output",
-		 mi_cmd_fix_multi_location_breakpoint_output),
-  DEF_MI_CMD_MI ("gdb-exit", mi_cmd_gdb_exit),
-  DEF_MI_CMD_CLI_1 ("gdb-set", "set", 1,
-		    &mi_suppress_notification.cmd_param_changed),
-  DEF_MI_CMD_CLI ("gdb-show", "show", 1),
-  DEF_MI_CMD_CLI ("gdb-version", "show version", 0),
-  DEF_MI_CMD_MI ("inferior-tty-set", mi_cmd_inferior_tty_set),
-  DEF_MI_CMD_MI ("inferior-tty-show", mi_cmd_inferior_tty_show),
-  DEF_MI_CMD_MI ("info-ada-exceptions", mi_cmd_info_ada_exceptions),
-  DEF_MI_CMD_MI ("info-gdb-mi-command", mi_cmd_info_gdb_mi_command),
-  DEF_MI_CMD_MI ("info-os", mi_cmd_info_os),
-  DEF_MI_CMD_MI ("interpreter-exec", mi_cmd_interpreter_exec),
-  DEF_MI_CMD_MI ("list-features", mi_cmd_list_features),
-  DEF_MI_CMD_MI ("list-target-features", mi_cmd_list_target_features),
-  DEF_MI_CMD_MI ("list-thread-groups", mi_cmd_list_thread_groups),
-  DEF_MI_CMD_MI ("remove-inferior", mi_cmd_remove_inferior),
-  DEF_MI_CMD_MI ("stack-info-depth", mi_cmd_stack_info_depth),
-  DEF_MI_CMD_MI ("stack-info-frame", mi_cmd_stack_info_frame),
-  DEF_MI_CMD_MI ("stack-list-arguments", mi_cmd_stack_list_args),
-  DEF_MI_CMD_MI ("stack-list-frames", mi_cmd_stack_list_frames),
-  DEF_MI_CMD_MI ("stack-list-locals", mi_cmd_stack_list_locals),
-  DEF_MI_CMD_MI ("stack-list-variables", mi_cmd_stack_list_variables),
-  DEF_MI_CMD_MI_1 ("stack-select-frame", mi_cmd_stack_select_frame,
-		   &mi_suppress_notification.user_selected_context),
-  DEF_MI_CMD_MI ("symbol-list-lines", mi_cmd_symbol_list_lines),
-  DEF_MI_CMD_CLI ("target-attach", "attach", 1),
-  DEF_MI_CMD_MI ("target-detach", mi_cmd_target_detach),
-  DEF_MI_CMD_CLI ("target-disconnect", "disconnect", 0),
-  DEF_MI_CMD_CLI ("target-download", "load", 1),
-  DEF_MI_CMD_MI ("target-file-delete", mi_cmd_target_file_delete),
-  DEF_MI_CMD_MI ("target-file-get", mi_cmd_target_file_get),
-  DEF_MI_CMD_MI ("target-file-put", mi_cmd_target_file_put),
-  DEF_MI_CMD_MI ("target-flash-erase", mi_cmd_target_flash_erase),
-  DEF_MI_CMD_CLI ("target-select", "target", 1),
-  DEF_MI_CMD_MI ("thread-info", mi_cmd_thread_info),
-  DEF_MI_CMD_MI ("thread-list-ids", mi_cmd_thread_list_ids),
-  DEF_MI_CMD_MI_1 ("thread-select", mi_cmd_thread_select,
-		   &mi_suppress_notification.user_selected_context),
-  DEF_MI_CMD_MI ("trace-define-variable", mi_cmd_trace_define_variable),
-  DEF_MI_CMD_MI_1 ("trace-find", mi_cmd_trace_find,
-		   &mi_suppress_notification.traceframe),
-  DEF_MI_CMD_MI ("trace-frame-collected",
-		 mi_cmd_trace_frame_collected),
-  DEF_MI_CMD_MI ("trace-list-variables", mi_cmd_trace_list_variables),
-  DEF_MI_CMD_MI ("trace-save", mi_cmd_trace_save),
-  DEF_MI_CMD_MI ("trace-start", mi_cmd_trace_start),
-  DEF_MI_CMD_MI ("trace-status", mi_cmd_trace_status),
-  DEF_MI_CMD_MI ("trace-stop", mi_cmd_trace_stop),
-  DEF_MI_CMD_MI ("var-assign", mi_cmd_var_assign),
-  DEF_MI_CMD_MI ("var-create", mi_cmd_var_create),
-  DEF_MI_CMD_MI ("var-delete", mi_cmd_var_delete),
-  DEF_MI_CMD_MI ("var-evaluate-expression", mi_cmd_var_evaluate_expression),
-  DEF_MI_CMD_MI ("var-info-path-expression", mi_cmd_var_info_path_expression),
-  DEF_MI_CMD_MI ("var-info-expression", mi_cmd_var_info_expression),
-  DEF_MI_CMD_MI ("var-info-num-children", mi_cmd_var_info_num_children),
-  DEF_MI_CMD_MI ("var-info-type", mi_cmd_var_info_type),
-  DEF_MI_CMD_MI ("var-list-children", mi_cmd_var_list_children),
-  DEF_MI_CMD_MI ("var-set-format", mi_cmd_var_set_format),
-  DEF_MI_CMD_MI ("var-set-frozen", mi_cmd_var_set_frozen),
-  DEF_MI_CMD_MI ("var-set-update-range", mi_cmd_var_set_update_range),
-  DEF_MI_CMD_MI ("var-set-visualizer", mi_cmd_var_set_visualizer),
-  DEF_MI_CMD_MI ("var-show-attributes", mi_cmd_var_show_attributes),
-  DEF_MI_CMD_MI ("var-show-format", mi_cmd_var_show_format),
-  DEF_MI_CMD_MI ("var-update", mi_cmd_var_update),
-  { NULL, }
-};
-
-/* Pointer to the mi command table (built at run time). */
-
-static struct mi_cmd **mi_table;
-
-/* A prime large enough to accomodate the entire command table.  */
-enum
-  {
-    MI_TABLE_SIZE = 227
-  };
-
-/* Exported function used to obtain info from the table.  */
-struct mi_cmd *
-mi_lookup (const char *command)
+  gdb_assert (command != NULL);
+  gdb_assert (command->name != NULL);
+
+  std::string name (command->name);
+
+  if (mi_cmd_table.find (name) != mi_cmd_table.end ())
+    return false;
+
+  mi_cmd_table[name] = std::move (command);
+
+  return true;
+}
+
+/* Create an mi_cmd structure with name NAME.  */
+
+static mi_cmd_up
+create_mi_cmd (const char *name)
 {
-  return *lookup_table (command);
+  mi_cmd_up cmd (new mi_cmd ());
+
+  cmd->name = name;
+
+  return cmd;
 }
 
-/* Used for collecting hash hit/miss statistics.  */
+/* Create and register a new MI command with a pure MI implementation.  */
 
-struct mi_cmd_stats
+static void
+add_mi_cmd_mi (const char *name, mi_cmd_argv_ftype function,
+	       int *suppress_notification = NULL)
 {
-  int hit;
-  int miss;
-  int rehash;
-};
-struct mi_cmd_stats stats;
+  mi_cmd_up cmd_up = create_mi_cmd (name);
 
-/* Look up a command.  */
+  cmd_up->cli.cmd = NULL;
+  cmd_up->cli.args_p = 0;
+  cmd_up->argv_func = function;
+  cmd_up->suppress_notification = suppress_notification;
 
-static struct mi_cmd **
-lookup_table (const char *command)
+  bool success = insert_mi_cmd_entry (std::move (cmd_up));
+  gdb_assert (success);
+}
+
+/* Create and register a new MI command implemented on top of a CLI command.  */
+
+static void
+add_mi_cmd_cli (const char *name, const char *cli_name, int args_p,
+		int *suppress_notification = NULL)
 {
-  const char *chp;
-  unsigned int index = 0;
-
-  /* Compute our hash.  */
-  for (chp = command; *chp; chp++)
-    {
-      /* We use a somewhat arbitrary formula.  */
-      index = ((index << 6) + (unsigned int) *chp) % MI_TABLE_SIZE;
-    }
-
-  while (1)
-    {
-      struct mi_cmd **entry = &mi_table[index];
-      if ((*entry) == 0)
-	{
-	  /* not found, return pointer to next free. */
-	  stats.miss++;
-	  return entry;
-	}
-      if (strcmp (command, (*entry)->name) == 0)
-	{
-	  stats.hit++;
-	  return entry;		/* found */
-	}
-      index = (index + 1) % MI_TABLE_SIZE;
-      stats.rehash++;
-    }
+  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 = NULL;
+  cmd_up->suppress_notification = suppress_notification;
+
+  bool success = insert_mi_cmd_entry (std::move (cmd_up));
+  gdb_assert (success);
 }
 
 static void
-build_table (struct mi_cmd *commands)
+build_table ()
+{
+  add_mi_cmd_mi ("ada-task-info", mi_cmd_ada_task_info);
+  add_mi_cmd_mi ("add-inferior", mi_cmd_add_inferior);
+  add_mi_cmd_cli ("break-after", "ignore", 1,
+		  &mi_suppress_notification.breakpoint);
+  add_mi_cmd_cli ("break-condition","cond", 1,
+		  &mi_suppress_notification.breakpoint);
+  add_mi_cmd_mi ("break-commands", mi_cmd_break_commands,
+		 &mi_suppress_notification.breakpoint);
+  add_mi_cmd_cli ("break-delete", "delete breakpoint", 1,
+		  &mi_suppress_notification.breakpoint);
+  add_mi_cmd_cli ("break-disable", "disable breakpoint", 1,
+		  &mi_suppress_notification.breakpoint);
+  add_mi_cmd_cli ("break-enable", "enable breakpoint", 1,
+		  &mi_suppress_notification.breakpoint);
+  add_mi_cmd_cli ("break-info", "info break", 1);
+  add_mi_cmd_mi ("break-insert", mi_cmd_break_insert,
+		 &mi_suppress_notification.breakpoint);
+  add_mi_cmd_mi ("dprintf-insert", mi_cmd_dprintf_insert,
+		 &mi_suppress_notification.breakpoint);
+  add_mi_cmd_cli ("break-list", "info break", 0);
+  add_mi_cmd_mi ("break-passcount", mi_cmd_break_passcount,
+		 &mi_suppress_notification.breakpoint);
+  add_mi_cmd_mi ("break-watch", mi_cmd_break_watch,
+		 &mi_suppress_notification.breakpoint);
+  add_mi_cmd_mi ("catch-assert", mi_cmd_catch_assert,
+		 &mi_suppress_notification.breakpoint);
+  add_mi_cmd_mi ("catch-exception", mi_cmd_catch_exception,
+		 &mi_suppress_notification.breakpoint);
+  add_mi_cmd_mi ("catch-handlers", mi_cmd_catch_handlers,
+		 &mi_suppress_notification.breakpoint);		 
+  add_mi_cmd_mi ("catch-load", mi_cmd_catch_load,
+		 &mi_suppress_notification.breakpoint);
+  add_mi_cmd_mi ("catch-unload", mi_cmd_catch_unload,
+		 &mi_suppress_notification.breakpoint);
+  add_mi_cmd_mi ("complete", mi_cmd_complete);
+  add_mi_cmd_mi ("data-disassemble", mi_cmd_disassemble);
+  add_mi_cmd_mi ("data-evaluate-expression", mi_cmd_data_evaluate_expression);
+  add_mi_cmd_mi ("data-list-changed-registers",
+		 mi_cmd_data_list_changed_registers);
+  add_mi_cmd_mi ("data-list-register-names", mi_cmd_data_list_register_names);
+  add_mi_cmd_mi ("data-list-register-values",
+		 mi_cmd_data_list_register_values);
+  add_mi_cmd_mi ("data-read-memory", mi_cmd_data_read_memory);
+  add_mi_cmd_mi ("data-read-memory-bytes", mi_cmd_data_read_memory_bytes);
+  add_mi_cmd_mi ("data-write-memory", mi_cmd_data_write_memory,
+		 &mi_suppress_notification.memory);
+  add_mi_cmd_mi ("data-write-memory-bytes", mi_cmd_data_write_memory_bytes,
+		 &mi_suppress_notification.memory);
+  add_mi_cmd_mi ("data-write-register-values",
+		 mi_cmd_data_write_register_values);
+  add_mi_cmd_mi ("enable-timings", mi_cmd_enable_timings);
+  add_mi_cmd_mi ("enable-pretty-printing", mi_cmd_enable_pretty_printing);
+  add_mi_cmd_mi ("enable-frame-filters", mi_cmd_enable_frame_filters);
+  add_mi_cmd_mi ("environment-cd", mi_cmd_env_cd);
+  add_mi_cmd_mi ("environment-directory", mi_cmd_env_dir);
+  add_mi_cmd_mi ("environment-path", mi_cmd_env_path);
+  add_mi_cmd_mi ("environment-pwd", mi_cmd_env_pwd);
+  add_mi_cmd_cli ("exec-arguments", "set args", 1,
+		  &mi_suppress_notification.cmd_param_changed);
+  add_mi_cmd_mi ("exec-continue", mi_cmd_exec_continue);
+  add_mi_cmd_mi ("exec-finish", mi_cmd_exec_finish);
+  add_mi_cmd_mi ("exec-jump", mi_cmd_exec_jump);
+  add_mi_cmd_mi ("exec-interrupt", mi_cmd_exec_interrupt);
+  add_mi_cmd_mi ("exec-next", mi_cmd_exec_next);
+  add_mi_cmd_mi ("exec-next-instruction", mi_cmd_exec_next_instruction);
+  add_mi_cmd_mi ("exec-return", mi_cmd_exec_return);
+  add_mi_cmd_mi ("exec-run", mi_cmd_exec_run);
+  add_mi_cmd_mi ("exec-step", mi_cmd_exec_step);
+  add_mi_cmd_mi ("exec-step-instruction", mi_cmd_exec_step_instruction);
+  add_mi_cmd_cli ("exec-until", "until", 1);
+  add_mi_cmd_cli ("file-exec-and-symbols", "file", 1);
+  add_mi_cmd_cli ("file-exec-file", "exec-file", 1);
+  add_mi_cmd_mi ("file-list-exec-source-file",
+		 mi_cmd_file_list_exec_source_file);
+  add_mi_cmd_mi ("file-list-exec-source-files",
+		 mi_cmd_file_list_exec_source_files);
+  add_mi_cmd_mi ("file-list-shared-libraries",
+     mi_cmd_file_list_shared_libraries),
+  add_mi_cmd_cli ("file-symbol-file", "symbol-file", 1);
+  add_mi_cmd_mi ("fix-multi-location-breakpoint-output",
+		 mi_cmd_fix_multi_location_breakpoint_output),
+  add_mi_cmd_mi ("gdb-exit", mi_cmd_gdb_exit);
+  add_mi_cmd_cli ("gdb-set", "set", 1,
+		  &mi_suppress_notification.cmd_param_changed);
+  add_mi_cmd_cli ("gdb-show", "show", 1);
+  add_mi_cmd_cli ("gdb-version", "show version", 0);
+  add_mi_cmd_mi ("inferior-tty-set", mi_cmd_inferior_tty_set);
+  add_mi_cmd_mi ("inferior-tty-show", mi_cmd_inferior_tty_show);
+  add_mi_cmd_mi ("info-ada-exceptions", mi_cmd_info_ada_exceptions);
+  add_mi_cmd_mi ("info-gdb-mi-command", mi_cmd_info_gdb_mi_command);
+  add_mi_cmd_mi ("info-os", mi_cmd_info_os);
+  add_mi_cmd_mi ("interpreter-exec", mi_cmd_interpreter_exec);
+  add_mi_cmd_mi ("list-features", mi_cmd_list_features);
+  add_mi_cmd_mi ("list-target-features", mi_cmd_list_target_features);
+  add_mi_cmd_mi ("list-thread-groups", mi_cmd_list_thread_groups);
+  add_mi_cmd_mi ("remove-inferior", mi_cmd_remove_inferior);
+  add_mi_cmd_mi ("stack-info-depth", mi_cmd_stack_info_depth);
+  add_mi_cmd_mi ("stack-info-frame", mi_cmd_stack_info_frame);
+  add_mi_cmd_mi ("stack-list-arguments", mi_cmd_stack_list_args);
+  add_mi_cmd_mi ("stack-list-frames", mi_cmd_stack_list_frames);
+  add_mi_cmd_mi ("stack-list-locals", mi_cmd_stack_list_locals);
+  add_mi_cmd_mi ("stack-list-variables", mi_cmd_stack_list_variables);
+  add_mi_cmd_mi ("stack-select-frame", mi_cmd_stack_select_frame,
+		 &mi_suppress_notification.user_selected_context);
+  add_mi_cmd_mi ("symbol-list-lines", mi_cmd_symbol_list_lines);
+  add_mi_cmd_cli ("target-attach", "attach", 1);
+  add_mi_cmd_mi ("target-detach", mi_cmd_target_detach);
+  add_mi_cmd_cli ("target-disconnect", "disconnect", 0);
+  add_mi_cmd_cli ("target-download", "load", 1);
+  add_mi_cmd_mi ("target-file-delete", mi_cmd_target_file_delete);
+  add_mi_cmd_mi ("target-file-get", mi_cmd_target_file_get);
+  add_mi_cmd_mi ("target-file-put", mi_cmd_target_file_put);
+  add_mi_cmd_mi ("target-flash-erase", mi_cmd_target_flash_erase);
+  add_mi_cmd_cli ("target-select", "target", 1);
+  add_mi_cmd_mi ("thread-info", mi_cmd_thread_info);
+  add_mi_cmd_mi ("thread-list-ids", mi_cmd_thread_list_ids);
+  add_mi_cmd_mi ("thread-select", mi_cmd_thread_select,
+		 &mi_suppress_notification.user_selected_context);
+  add_mi_cmd_mi ("trace-define-variable", mi_cmd_trace_define_variable);
+  add_mi_cmd_mi ("trace-find", mi_cmd_trace_find,
+		 &mi_suppress_notification.traceframe);
+  add_mi_cmd_mi ("trace-frame-collected", mi_cmd_trace_frame_collected);
+  add_mi_cmd_mi ("trace-list-variables", mi_cmd_trace_list_variables);
+  add_mi_cmd_mi ("trace-save", mi_cmd_trace_save);
+  add_mi_cmd_mi ("trace-start", mi_cmd_trace_start);
+  add_mi_cmd_mi ("trace-status", mi_cmd_trace_status);
+  add_mi_cmd_mi ("trace-stop", mi_cmd_trace_stop);
+  add_mi_cmd_mi ("var-assign", mi_cmd_var_assign);
+  add_mi_cmd_mi ("var-create", mi_cmd_var_create);
+  add_mi_cmd_mi ("var-delete", mi_cmd_var_delete);
+  add_mi_cmd_mi ("var-evaluate-expression", mi_cmd_var_evaluate_expression);
+  add_mi_cmd_mi ("var-info-path-expression", mi_cmd_var_info_path_expression);
+  add_mi_cmd_mi ("var-info-expression", mi_cmd_var_info_expression);
+  add_mi_cmd_mi ("var-info-num-children", mi_cmd_var_info_num_children);
+  add_mi_cmd_mi ("var-info-type", mi_cmd_var_info_type);
+  add_mi_cmd_mi ("var-list-children", mi_cmd_var_list_children);
+  add_mi_cmd_mi ("var-set-format", mi_cmd_var_set_format);
+  add_mi_cmd_mi ("var-set-frozen", mi_cmd_var_set_frozen);
+  add_mi_cmd_mi ("var-set-update-range", mi_cmd_var_set_update_range);
+  add_mi_cmd_mi ("var-set-visualizer", mi_cmd_var_set_visualizer);
+  add_mi_cmd_mi ("var-show-attributes", mi_cmd_var_show_attributes);
+  add_mi_cmd_mi ("var-show-format", mi_cmd_var_show_format);
+  add_mi_cmd_mi ("var-update", mi_cmd_var_update);
+}
+
+/* See mi-cmds.h.  */
+
+struct mi_cmd *
+mi_cmd_lookup (const char *command)
 {
-  int nr_rehash = 0;
-  int nr_entries = 0;
-  struct mi_cmd *command;
-
-  mi_table = XCNEWVEC (struct mi_cmd *, MI_TABLE_SIZE);
-  for (command = commands; command->name != 0; command++)
-    {
-      struct mi_cmd **entry = lookup_table (command->name);
-
-      if (*entry)
-	internal_error (__FILE__, __LINE__,
-			_("command `%s' appears to be duplicated"),
-			command->name);
-      *entry = command;
-      /* FIXME lose these prints */
-      if (0)
-	{
-	  fprintf_unfiltered (gdb_stdlog, "%-30s %2d\n",
-			      command->name, stats.rehash - nr_rehash);
-	}
-      nr_entries++;
-      nr_rehash = stats.rehash;
-    }
-  if (0)
-    {
-      fprintf_filtered (gdb_stdlog, "Average %3.1f\n",
-			(double) nr_rehash / (double) nr_entries);
-    }
+  gdb_assert (command != NULL);
+
+  auto it = mi_cmd_table.find (command);
+
+  if (it == mi_cmd_table.end ())
+    return NULL;
+
+  return it->second.get ();
 }
 
 void
 _initialize_mi_cmds (void)
 {
-  build_table (mi_cmds);
-  memset (&stats, 0, sizeof (stats));
+  build_table ();
 }
diff --git a/gdb/mi/mi-cmds.h b/gdb/mi/mi-cmds.h
index 58aa2d6f77..becd788d0f 100644
--- a/gdb/mi/mi-cmds.h
+++ b/gdb/mi/mi-cmds.h
@@ -154,9 +154,11 @@ struct mi_cmd
   int *suppress_notification;
 };
 
+typedef std::unique_ptr<struct mi_cmd> mi_cmd_up;
+
 /* Lookup a command in the MI command table.  */
 
-extern struct mi_cmd *mi_lookup (const char *command);
+extern struct mi_cmd *mi_cmd_lookup (const char *command);
 
 /* Debug flag */
 extern int mi_debug_p;
diff --git a/gdb/mi/mi-parse.c b/gdb/mi/mi-parse.c
index cc6a4419d0..6c8d922d66 100644
--- a/gdb/mi/mi-parse.c
+++ b/gdb/mi/mi-parse.c
@@ -272,7 +272,7 @@ mi_parse (const char *cmd, char **token)
   }
 
   /* Find the command in the MI table.  */
-  parse->cmd = mi_lookup (parse->command);
+  parse->cmd = mi_cmd_lookup (parse->command);
   if (parse->cmd == NULL)
     throw_error (UNDEFINED_COMMAND_ERROR,
 		 _("Undefined MI command: %s"), parse->command);
-- 
2.20.1

^ permalink raw reply	[flat|nested] 50+ messages in thread

* [PATCH v3 0/5] Create MI commands using python.
  2019-01-28 12:41   ` [PATCH v2 0/2] " Jan Vrany
                       ` (9 preceding siblings ...)
  2019-04-18 11:59     ` [PATCH v4 0/2] " Jan Vrany
@ 2019-05-30 13:49     ` Jan Vrany
  2019-06-10 12:20       ` Jan Vrany
  2019-05-30 13:49     ` [PATCH v3 1/5] Use std::map for MI commands in mi-cmds.c Jan Vrany
                       ` (4 subsequent siblings)
  15 siblings, 1 reply; 50+ messages in thread
From: Jan Vrany @ 2019-05-30 13:49 UTC (permalink / raw)
  To: gdb-patches; +Cc: Jan Vrany

This version mainly addresses comments raised by Simon. Tested with both
Python 2.7 and Python 3(.7).

Changes v2 -> v3

* addressed Simon's comments:
  - https://sourceware.org/ml/gdb-patches/2019-05/msg00406.html
  - https://sourceware.org/ml/gdb-patches/2019-05/msg00407.html
* checked that it compiles with both Python 2.7 and Python 3(.7)
* add tests for MI command returning dictionary with non-string
  keys
* add tests for creating MI commands with invalid names

Changes v1 -> v2

* squashed following commits into one:
  - 8e65585bdab0 Create MI commands using python.
  - 9e2afadd2d4d03 mi/python: C++ify python MI command handling code
  - 0491a0634f7de4 mi/python: Polish MI output of python commands
* moved class mi_command_py to python code
* use gdbpy_ref<> instead of void* to hold on Python object
  in mi_command_py
* release python objects held on by mi_command_py objects
  when finalizing Python
* merged py_mi_invoke() into mi_command_py::invoke()
* added missing copyright headers
* dropped micmdpy_parse_command_name()
* prefixed error messages by MI command name except for
  "Problem parsing arguments:" since old C-implemented MI
  commands do not prefix command name.
* make do_suppress_notification() to return gdb::optional
* split invoke to invoke and do_invoke, making invoke to perform
  common initialization
* add test redefining Python MI command while the very same command
  is running
* documented the fact that in mi_command_py::do_invoke() `this` pointer cannot
  be used after invoking python code since it may got stale.


Didier Nadeau (3):
  Use std::map for MI commands in mi-cmds.c
  Use classes to represent MI Command instead of structures
  Create MI commands using python.

Jan Vrany (2):
  mi/python: Allow redefinition of python MI commands
  mi/python: Add tests for python-defined MI commands

 gdb/ChangeLog                          |  88 +++++
 gdb/Makefile.in                        |   1 +
 gdb/mi/mi-cmd-info.c                   |   4 +-
 gdb/mi/mi-cmds.c                       | 510 +++++++++++++------------
 gdb/mi/mi-cmds.h                       |  87 ++++-
 gdb/mi/mi-main.c                       |  22 +-
 gdb/mi/mi-main.h                       |   1 +
 gdb/mi/mi-parse.c                      |  20 +-
 gdb/mi/mi-parse.h                      |   6 +-
 gdb/python/py-micmd.c                  | 307 +++++++++++++++
 gdb/python/py-micmd.h                  |  60 +++
 gdb/python/python-internal.h           |   2 +
 gdb/python/python.c                    |  13 +-
 gdb/testsuite/ChangeLog                |   5 +
 gdb/testsuite/gdb.python/py-mi-cmd.exp | 132 +++++++
 gdb/testsuite/gdb.python/py-mi-cmd.py  |  54 +++
 16 files changed, 1007 insertions(+), 305 deletions(-)
 create mode 100644 gdb/python/py-micmd.c
 create mode 100644 gdb/python/py-micmd.h
 create mode 100644 gdb/testsuite/gdb.python/py-mi-cmd.exp
 create mode 100644 gdb/testsuite/gdb.python/py-mi-cmd.py

-- 
2.20.1

^ permalink raw reply	[flat|nested] 50+ messages in thread

* [PATCH v3 4/5] mi/python: Allow redefinition of python MI commands
  2019-01-28 12:41   ` [PATCH v2 0/2] " Jan Vrany
                       ` (11 preceding siblings ...)
  2019-05-30 13:49     ` [PATCH v3 1/5] Use std::map for MI commands in mi-cmds.c Jan Vrany
@ 2019-05-30 13:49     ` Jan Vrany
  2019-06-18 20:03       ` Pedro Alves
  2019-05-30 13:49     ` [PATCH v3 2/5] Use classes to represent MI Command instead of structures Jan Vrany
                       ` (2 subsequent siblings)
  15 siblings, 1 reply; 50+ messages in thread
From: Jan Vrany @ 2019-05-30 13:49 UTC (permalink / raw)
  To: gdb-patches; +Cc: Jan Vrany

Redefining python MI commands is especially useful when developing
them.

gdb/Changelog:

	* mi/mi-cmds.h (mi_command::can_be_redefined): New method.
	* mi/mi-cmds.c: (mi_command::can_be_redefined): New method.
	(insert_mi_cmd_entry): Allow redefinition of python-defined MI commands.
	* python/py-micmd.h (mi_command_py::can_be_redefined): New method.
	* python/py-micmd.c: (mi_command_py::can_be_redefined): New method.
---
 gdb/ChangeLog         |  8 ++++++++
 gdb/mi/mi-cmds.c      | 10 +++++++++-
 gdb/mi/mi-cmds.h      |  3 +++
 gdb/python/py-micmd.c | 24 +++++++++++++++++-------
 gdb/python/py-micmd.h |  2 +-
 5 files changed, 38 insertions(+), 9 deletions(-)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 241e5da68f..1d264c44f7 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,11 @@
+2019-05-02  Jan Vrany  <jan.vrany@fit.cvut.cz>
+
+	* mi/mi-cmds.h (mi_command::can_be_redefined): New method.
+	* mi/mi-cmds.c: (mi_command::can_be_redefined): New method.
+	(insert_mi_cmd_entry): Allow redefinition of python-defined MI commands.
+	* python/py-micmd.h (mi_command_py::can_be_redefined): New method.
+	* python/py-micmd.c: (mi_command_py::can_be_redefined): New method.
+
 2019-05-02  Didier Nadeau  <didier.nadeau@gmail.com>
             Jan Vrany  <jan.vrany@fit.cvut.cz>
 
diff --git a/gdb/mi/mi-cmds.c b/gdb/mi/mi-cmds.c
index 0aead954f9..e280e51daf 100644
--- a/gdb/mi/mi-cmds.c
+++ b/gdb/mi/mi-cmds.c
@@ -41,7 +41,8 @@ insert_mi_cmd_entry (mi_cmd_up command)
   const std::string &name = command->name ();
 
   if (mi_cmd_table.find (name) != mi_cmd_table.end ())
-    return false;
+    if (! mi_cmd_table[name]->can_be_redefined ())
+      return false;
 
   mi_cmd_table[name] = std::move (command);
 
@@ -80,6 +81,13 @@ mi_command::mi_command (const char *name, int *suppress_notification)
     m_suppress_notification (suppress_notification)
 {}
 
+bool
+mi_command::can_be_redefined()
+{
+  return false;
+}
+
+
 
 void
 mi_command::invoke (struct mi_parse *parse)
diff --git a/gdb/mi/mi-cmds.h b/gdb/mi/mi-cmds.h
index 5ca7232fca..331b1349df 100644
--- a/gdb/mi/mi-cmds.h
+++ b/gdb/mi/mi-cmds.h
@@ -143,6 +143,9 @@ public:
   /* Execute the MI command.  */
   void invoke (struct mi_parse *parse);
 
+  /* Return TRUE if command can be redefined, FALSE otherwise. */
+  virtual bool can_be_redefined();
+
 protected:
   gdb::optional<scoped_restore_tmpl<int>> do_suppress_notification ();
   virtual void do_invoke(struct mi_parse *parse) = 0;
diff --git a/gdb/python/py-micmd.c b/gdb/python/py-micmd.c
index e99632c97a..4f2b13f3c7 100644
--- a/gdb/python/py-micmd.c
+++ b/gdb/python/py-micmd.c
@@ -171,6 +171,12 @@ mi_command_py::mi_command_py (const char *name, gdbpy_ref<> object)
 {
 }
 
+bool
+mi_command_py::can_be_redefined()
+{
+  return true;
+}
+
 void
 mi_command_py::do_invoke (struct mi_parse *parse)
 {
@@ -179,6 +185,7 @@ mi_command_py::do_invoke (struct mi_parse *parse)
   if (parse->argv == NULL)
     error (_("Problem parsing arguments: %s %s"), parse->command, parse->args);
 
+  std::string name (this->name ());
   PyObject *obj = this->pyobj.get ();
 
   gdbpy_enter enter_py (get_current_arch (), current_language);
@@ -187,15 +194,14 @@ mi_command_py::do_invoke (struct mi_parse *parse)
 
   if (!PyObject_HasAttr (obj, invoke_cst))
       error (_("-%s: Python command object missing 'invoke' method."),
-	     name ().c_str ());
-
+	     name.c_str ());
 
   gdbpy_ref<> argobj (PyList_New (parse->argc));
   if (argobj == nullptr)
     {
       gdbpy_print_stack ();
       error (_("-%s: failed to create the Python arguments list."),
-	     name ().c_str ());
+	     name.c_str ());
     }
 
   for (int i = 0; i < parse->argc; ++i)
@@ -205,11 +211,16 @@ mi_command_py::do_invoke (struct mi_parse *parse)
       if (PyList_SetItem (argobj.get (), i, str.release ()) != 0)
 	{
 	  error (_("-%s: failed to create the Python arguments list."),
-		 name ().c_str ());
+		 name.c_str ());
 	}
     }
 
   gdb_assert (PyErr_Occurred () == NULL);
+
+  /* From this point on, THIS must not be used since Python code may replace
+     the the very same command that is currently executing.  This in turn leads
+     to desctruction of THIS making it invalid.  See insert_mi_cmd_entry. */
+
   gdbpy_ref<> result (
     PyObject_CallMethodObjArgs (obj, invoke_cst, argobj.get (), NULL));
   if (PyErr_Occurred () != NULL)
@@ -218,9 +229,9 @@ mi_command_py::do_invoke (struct mi_parse *parse)
       gdb::unique_xmalloc_ptr<char> ex_msg (ex.to_string ());
 
       if (ex_msg == NULL || *ex_msg == '\0')
-	error (_("-%s: failed to execute command"), name ().c_str ());
+	error (_("-%s: failed to execute command"), name.c_str ());
       else
-	error (_("-%s: %s"), name ().c_str (), ex_msg.get ());
+	error (_("-%s: %s"), name.c_str (), ex_msg.get ());
     }
   else
     {
@@ -233,7 +244,6 @@ void mi_command_py::finalize ()
 {
   this->pyobj.reset (nullptr);
 }
-
 /* Initialize the MI command object.  */
 
 int
diff --git a/gdb/python/py-micmd.h b/gdb/python/py-micmd.h
index deadc0116b..0d61069ca5 100644
--- a/gdb/python/py-micmd.h
+++ b/gdb/python/py-micmd.h
@@ -44,7 +44,7 @@ class mi_command_py : public mi_command
 
     mi_command_py (const char *name, gdbpy_ref<> object);
 
-
+    bool can_be_redefined() override;
     /* This is called just before shutting down a Python interpreter
        to release python object implementing the command. */
 
-- 
2.20.1

^ permalink raw reply	[flat|nested] 50+ messages in thread

* [PATCH v3 2/5] Use classes to represent MI Command instead of structures
  2019-01-28 12:41   ` [PATCH v2 0/2] " Jan Vrany
                       ` (12 preceding siblings ...)
  2019-05-30 13:49     ` [PATCH v3 4/5] mi/python: Allow redefinition of python MI commands Jan Vrany
@ 2019-05-30 13:49     ` Jan Vrany
  2019-06-18 19:38       ` Pedro Alves
  2019-05-30 13:49     ` [PATCH v3 3/5] Create MI commands using python Jan Vrany
  2019-05-30 14:19     ` [PATCH v3 5/5] mi/python: Add tests for python-defined MI commands Jan Vrany
  15 siblings, 1 reply; 50+ messages in thread
From: Jan Vrany @ 2019-05-30 13:49 UTC (permalink / raw)
  To: gdb-patches; +Cc: Didier Nadeau

From: Didier Nadeau <didier.nadeau@gmail.com>

This commit changes the infrastructure of mi-cmds.h and associated
files to use classes instead of structure to populate the hashmap
containing the commands.

The base class is virtual and there is one subclass MI commands
implemented with pure MI implementation and another for MI commands
implemented over a CLI command.

Logic for suppress_notification and parsing of ARGV/ARGC has been moved
to the classes implementation.

gdb/ChangeLog:

	* mi/mi-cmds.c (create_mi_cmd): Remove.
	(mi_command::mi_command): New function.
	(mi_command::do_suppress_notification): New function.
	(mi_command::invoke): New function.
	(mi_command_mi::mi_command_mi): New function.
	(mi_command_mi::do_invoke): New function.
	(mi_command_cli::mi_command_cli): New function.
	(mi_command_cli::do_invoke): New function.
	(mi_cmd_lookup): Change return type.
	* mi/mi-cmds.h (struct mi_cli): Remove.
	(struct mi_cmd): Remove.
	(class mi_command): New class.
	(class mi_command_mi): New class.
	(class mi_command_cli): New class.
	(mi_cmd_loopkup): Change return type.
	* mi/mi-main.c (mi_execute_cli_command): Remove declaration.
	(mi_execute_command): Remove suppress_notification handling.
	(mi_cmd_execute): Remove call to argv_func.
	(mi_cmd_execute): Remove call to mi_execute_cli_command.
	(mi_cmd_execute): New call to mi_command::invoke.
	* mi/mi-main.h (mi_execute_cli_command): New declaration.
	* mi/mi-parse.c (mi_parse): Remove call to mi_parse_argv.
	* mi/mi-parse.h (struct mi_parse): Remove field struct mi_cmd.
	(struct mi_parse): New field class mi_command.
	(mi_parse_argv): New declaration.
---
 gdb/ChangeLog        |  29 +++++++++++++
 gdb/mi/mi-cmd-info.c |   2 +-
 gdb/mi/mi-cmds.c     | 100 ++++++++++++++++++++++++++++++-------------
 gdb/mi/mi-cmds.h     |  75 ++++++++++++++++++++++----------
 gdb/mi/mi-main.c     |  22 +---------
 gdb/mi/mi-main.h     |   1 +
 gdb/mi/mi-parse.c    |  18 ++------
 gdb/mi/mi-parse.h    |   6 ++-
 8 files changed, 164 insertions(+), 89 deletions(-)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 8e964c0adb..994ad947bd 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,32 @@
+2019-05-02  Didier Nadeau  <didier.nadeau@gmail.com>
+	    Jan Vrany <jan.vrany@fit.cvut.cz>
+
+	* mi/mi-cmds.c (create_mi_cmd): Remove.
+	(mi_command::mi_command): New function.
+	(mi_command::do_suppress_notification): New function.
+	(mi_command::invoke): New function.
+	(mi_command_mi::mi_command_mi): New function.
+	(mi_command_mi::do_invoke): New function.
+	(mi_command_cli::mi_command_cli): New function.
+	(mi_command_cli::do_invoke): New function.
+	(mi_cmd_lookup): Change return type.
+	* mi/mi-cmds.h (struct mi_cli): Remove.
+	(struct mi_cmd): Remove.
+	(class mi_command): New class.
+	(class mi_command_mi): New class.
+	(class mi_command_cli): New class.
+	(mi_cmd_loopkup): Change return type.
+	* mi/mi-main.c (mi_execute_cli_command): Remove declaration.
+	(mi_execute_command): Remove suppress_notification handling.
+	(mi_cmd_execute): Remove call to argv_func.
+	(mi_cmd_execute): Remove call to mi_execute_cli_command.
+	(mi_cmd_execute): New call to mi_command::invoke.
+	* mi/mi-main.h (mi_execute_cli_command): New declaration.
+	* mi/mi-parse.c (mi_parse): Remove call to mi_parse_argv.
+	* mi/mi-parse.h (struct mi_parse): Remove field struct mi_cmd.
+	(struct mi_parse): New field class mi_command.
+	(mi_parse_argv): New declaration.
+
 2019-05-02  Didier Nadeau  <didier.nadeau@gmail.com>
             Jan Vrany <jan.vrany@fit.cvut.cz>
 
diff --git a/gdb/mi/mi-cmd-info.c b/gdb/mi/mi-cmd-info.c
index 8645fac294..3d8bd68264 100644
--- a/gdb/mi/mi-cmd-info.c
+++ b/gdb/mi/mi-cmd-info.c
@@ -67,7 +67,7 @@ void
 mi_cmd_info_gdb_mi_command (const char *command, char **argv, int argc)
 {
   const char *cmd_name;
-  struct mi_cmd *cmd;
+  mi_command *cmd;
   struct ui_out *uiout = current_uiout;
 
   /* This command takes exactly one argument.  */
diff --git a/gdb/mi/mi-cmds.c b/gdb/mi/mi-cmds.c
index 13db22afaf..2d8863c5f9 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 <map>
 #include <string>
 
@@ -36,9 +37,9 @@ static bool
 insert_mi_cmd_entry (mi_cmd_up command)
 {
   gdb_assert (command != NULL);
-  gdb_assert (command->name != NULL);
+  gdb_assert (!command->name ().empty ());
 
-  std::string name (command->name);
+  const std::string &name = command->name ();
 
   if (mi_cmd_table.find (name) != mi_cmd_table.end ())
     return false;
@@ -48,32 +49,16 @@ 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 a pure MI implementation.  */
 
 static void
 add_mi_cmd_mi (const char *name, mi_cmd_argv_ftype function,
 	       int *suppress_notification = NULL)
 {
-  mi_cmd_up cmd_up = create_mi_cmd (name);
-
-  cmd_up->cli.cmd = NULL;
-  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);
 }
 
@@ -83,17 +68,72 @@ static void
 add_mi_cmd_cli (const char *name, const char *cli_name, int args_p,
 		int *suppress_notification = NULL)
 {
-  mi_cmd_up cmd_up = create_mi_cmd (name);
+  mi_cmd_up command (new mi_command_cli (name, cli_name, args_p,
+                                         suppress_notification));
 
-  cmd_up->cli.cmd = cli_name;
-  cmd_up->cli.args_p = args_p;
-  cmd_up->argv_func = NULL;
-  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);
 }
 
+/* See mi-cmds.h  */
+mi_command::mi_command (const char *name, int *suppress_notification)
+  : m_name (name),
+    m_suppress_notification (suppress_notification)
+{}
+
+
+void
+mi_command::invoke (struct mi_parse *parse)
+{
+  gdb::optional<scoped_restore_tmpl<int>> restore
+    = do_suppress_notification ();
+  this->do_invoke (parse);
+}
+
+gdb::optional<scoped_restore_tmpl<int>>
+mi_command::do_suppress_notification ()
+{
+  if (m_suppress_notification != NULL)
+    return scoped_restore_tmpl<int> (m_suppress_notification, 1);
+  else
+    return {};
+}
+
+mi_command_mi::mi_command_mi (const char *name, mi_cmd_argv_ftype func,
+			      int *suppress_notification)
+  : mi_command (name, suppress_notification),
+    m_argv_function (func)
+{
+  gdb_assert (func != NULL);
+}
+
+void
+mi_command_mi::do_invoke (struct mi_parse *parse)
+{
+  mi_parse_argv (parse->args, parse);
+
+  if (parse->argv == NULL)
+    error (_("Problem parsing arguments: %s %s"), parse->command, parse->args);
+
+  this->m_argv_function (parse->command, parse->argv, parse->argc);
+}
+
+mi_command_cli::mi_command_cli (const char *name, const char *cli_name,
+				int args_p, int *suppress_notification)
+  : mi_command (name, suppress_notification),
+    m_cli_name (cli_name),
+    m_args_p (args_p)
+{}
+
+void
+mi_command_cli::do_invoke (struct mi_parse *parse)
+{
+  mi_execute_cli_command (this->m_cli_name.c_str (), this->m_args_p,
+			  parse->args);
+}
+
+/* Initialize the available MI commands.  */
+
 static void
 build_table ()
 {
@@ -126,7 +166,7 @@ build_table ()
   add_mi_cmd_mi ("catch-exception", mi_cmd_catch_exception,
 		 &mi_suppress_notification.breakpoint);
   add_mi_cmd_mi ("catch-handlers", mi_cmd_catch_handlers,
-		 &mi_suppress_notification.breakpoint);		 
+		 &mi_suppress_notification.breakpoint);
   add_mi_cmd_mi ("catch-load", mi_cmd_catch_load,
 		 &mi_suppress_notification.breakpoint);
   add_mi_cmd_mi ("catch-unload", mi_cmd_catch_unload,
@@ -244,7 +284,7 @@ build_table ()
 
 /* See mi-cmds.h.  */
 
-struct mi_cmd *
+mi_command *
 mi_cmd_lookup (const char *command)
 {
   gdb_assert (command != NULL);
diff --git a/gdb/mi/mi-cmds.h b/gdb/mi/mi-cmds.h
index becd788d0f..f1b4728bde 100644
--- a/gdb/mi/mi-cmds.h
+++ b/gdb/mi/mi-cmds.h
@@ -127,38 +127,69 @@ 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.  */
+/* mi_command base virtual class.  */
 
-struct mi_cli
+class mi_command
 {
-  /* 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;
+public:
+  mi_command (const char *name, int *suppress_notification);
+  virtual ~mi_command () {};
+
+  const std::string &name ()
+  { return m_name; }
+
+  /* Execute the MI command.  */
+  void invoke (struct mi_parse *parse);
+
+protected:
+  gdb::optional<scoped_restore_tmpl<int>> do_suppress_notification ();
+  virtual void do_invoke(struct mi_parse *parse) = 0;
+
+private:
+
+  /* The name of the command.  */
+  std::string m_name;
+
+  /* Pointer to integer to set during command's invocation.  */
+  int *m_suppress_notification;
 };
 
-struct mi_cmd
+/* MI command with a pure MI implementation.  */
+
+class mi_command_mi : public mi_command
 {
-  /* 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;
+public:
+  mi_command_mi (const char *name, mi_cmd_argv_ftype func,
+                 int *suppress_notification);
+
+protected:
+  void do_invoke(struct mi_parse *parse) override;
+
+private:
+  mi_cmd_argv_ftype *m_argv_function;
+};
+
+/* MI command implemented on top of a CLI command.  */
+
+class mi_command_cli : public mi_command
+{
+public:
+  mi_command_cli (const char *name, const char *cli_name, int args_p,
+                  int *suppress_notification);
+
+protected:
+  void do_invoke(struct mi_parse *parse) override;
+
+private:
+  std::string m_cli_name;
+  int m_args_p;
 };
 
-typedef std::unique_ptr<struct mi_cmd> mi_cmd_up;
+typedef std::unique_ptr<mi_command> mi_cmd_up;
 
 /* Lookup a command in the MI command table.  */
 
-extern struct mi_cmd *mi_cmd_lookup (const char *command);
+extern mi_command *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 4921c13528..921d0b5a03 100644
--- a/gdb/mi/mi-main.c
+++ b/gdb/mi/mi-main.c
@@ -90,9 +90,6 @@ int running_result_record_printed = 1;
 int mi_proceeded;
 
 static void mi_cmd_execute (struct mi_parse *parse);
-
-static void mi_execute_cli_command (const char *cmd, int 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 *,
@@ -1952,11 +1949,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)
@@ -2095,18 +2087,8 @@ 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.  */
-      mi_execute_cli_command (parse->cmd->cli.cmd, parse->cmd->cli.args_p,
-			      parse->args);
-    }
+  if (parse->cmd != NULL)
+    parse->cmd->invoke (parse);
   else
     {
       /* FIXME: DELETE THIS.  */
diff --git a/gdb/mi/mi-main.h b/gdb/mi/mi-main.h
index 1986228d22..90d54a6eed 100644
--- a/gdb/mi/mi-main.h
+++ b/gdb/mi/mi-main.h
@@ -54,6 +54,7 @@ struct mi_suppress_notification
 };
 extern struct mi_suppress_notification mi_suppress_notification;
 
+void mi_execute_cli_command (const char *cmd, int 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 6c8d922d66..7ed2b2bbc6 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 2262ff56f9..94bd48219c 100644
--- a/gdb/mi/mi-parse.h
+++ b/gdb/mi/mi-parse.h
@@ -49,7 +49,7 @@ struct mi_parse
     enum mi_command_type op;
     char *command;
     char *token;
-    const struct mi_cmd *cmd;
+    mi_command *cmd;
     struct mi_timestamp *cmd_start;
     char *args;
     char **argv;
@@ -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.  */
+
+void mi_parse_argv (const char *args, struct mi_parse *parse);
+
 #endif /* MI_MI_PARSE_H */
-- 
2.20.1

^ permalink raw reply	[flat|nested] 50+ messages in thread

* [PATCH v3 5/5] mi/python: Add tests for python-defined MI commands
  2019-01-28 12:41   ` [PATCH v2 0/2] " Jan Vrany
                       ` (14 preceding siblings ...)
  2019-05-30 13:49     ` [PATCH v3 3/5] Create MI commands using python Jan Vrany
@ 2019-05-30 14:19     ` Jan Vrany
  2019-06-18 20:11       ` Pedro Alves
  15 siblings, 1 reply; 50+ messages in thread
From: Jan Vrany @ 2019-05-30 14:19 UTC (permalink / raw)
  To: gdb-patches; +Cc: Jan Vrany

gdb/testsuite/Changelog:

	* gdb.python/py-mi-cmd.exp: New file.
	* gdb.python/py-mi-cmd.py: New file.
---
 gdb/testsuite/ChangeLog                |   5 +
 gdb/testsuite/gdb.python/py-mi-cmd.exp | 132 +++++++++++++++++++++++++
 gdb/testsuite/gdb.python/py-mi-cmd.py  |  54 ++++++++++
 3 files changed, 191 insertions(+)
 create mode 100644 gdb/testsuite/gdb.python/py-mi-cmd.exp
 create mode 100644 gdb/testsuite/gdb.python/py-mi-cmd.py

diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index ba657cc615..26d14996f4 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,3 +1,8 @@
+2019-05-03  Jan Vrany  <jan.vrany@fit.cvut.cz>
+
+	* gdb.python/py-mi-cmd.exp: New file.
+	* gdb.python/py-mi-cmd.py: New file.
+
 2019-05-24  Tom de Vries  <tdevries@suse.de>
 
 	* gdb.dwarf2/gdb-add-index.exp: New file.
diff --git a/gdb/testsuite/gdb.python/py-mi-cmd.exp b/gdb/testsuite/gdb.python/py-mi-cmd.exp
new file mode 100644
index 0000000000..ba8670d5ea
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-mi-cmd.exp
@@ -0,0 +1,132 @@
+# Copyright (C) 2018 Free Software Foundation, Inc.
+# 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/>.
+
+# Test custom MI commands implemented in Python.
+
+load_lib gdb-python.exp
+load_lib mi-support.exp
+set MIFLAGS "-i=mi2"
+
+gdb_exit
+if {[mi_gdb_start]} {
+    continue
+}
+
+if {[lsearch -exact [mi_get_features] python] < 0} {
+    unsupported "python support is disabled"
+    return -1
+}
+
+standard_testfile
+#
+# Start here
+#
+
+
+mi_gdb_test "set python print-stack full" \
+  ".*\\^done" \
+  "set python print-stack full"
+
+mi_gdb_test "source ${srcdir}/${subdir}/${testfile}.py" \
+  ".*\\^done" \
+  "load python file"
+
+mi_gdb_test "python pycmd1('-pycmd')" \
+  ".*\\^done" \
+  "Define -pycmd MI command"
+
+
+mi_gdb_test "-pycmd int" \
+  "\\^done,result=\"42\"" \
+  "-pycmd int"
+
+mi_gdb_test "-pycmd str" \
+  "\\^done,result=\"Hello world!\"" \
+  "-pycmd str"
+
+mi_gdb_test "-pycmd ary" \
+  "\\^done,result=\\\[\"Hello\",\"42\"\\\]" \
+  "-pycmd ary"
+
+mi_gdb_test "-pycmd dct" \
+  "\\^done,result={hello=\"world\",times=\"42\"}" \
+  "-pycmd dct"
+
+mi_gdb_test "-pycmd bk1" \
+  "\\^error,msg=\"Non-string object used as key: Bad Kay.\"" \
+  "-pycmd bk1"
+
+mi_gdb_test "-pycmd bk2" \
+  "\\^error,msg=\"Non-string object used as key: 1.\"" \
+  "-pycmd bk2"
+
+mi_gdb_test "-pycmd bk3" \
+  "\\^error,msg=\"Non-string object used as key: __repr__ returned non-string .*" \
+  "-pycmd bk3"
+
+mi_gdb_test "-pycmd tpl" \
+  "\\^done,result=\\\[\"42\",\"Hello\"\\\]" \
+  "-pycmd tpl"
+
+mi_gdb_test "-pycmd itr" \
+  "\\^done,result=\\\[\"1\",\"2\",\"3\"\\\]" \
+  "-pycmd itr"
+
+mi_gdb_test "-pycmd nn1" \
+  "\\^done" \
+  "-pycmd nn1"
+
+mi_gdb_test "-pycmd nn2" \
+  "\\^done,result=\\\[\"None\"\\\]" \
+  "-pycmd nn2"
+
+mi_gdb_test "-pycmd bogus" \
+  "\\^error,msg=\"-pycmd: Invalid parameter: bogus\"" \
+  "-pycmd bogus"
+
+mi_gdb_test "-pycmd exp" \
+  "\\^error,msg=\"-pycmd: failed to execute command\"" \
+  "-pycmd exp"
+
+mi_gdb_test "python pycmd2('-pycmd')" \
+  ".*\\^done" \
+  "Redefine -pycmd MI command from CLI command"
+
+mi_gdb_test "-pycmd str" \
+  "\\^done,result=\"Ciao!\"" \
+  "-pycmd str"
+
+mi_gdb_test "-pycmd int" \
+  "\\^error,msg=\"-pycmd: Invalid parameter: int\"" \
+  "-pycmd int"
+
+mi_gdb_test "-pycmd red" \
+    "\\^error,msg=\"-pycmd: Command redefined but we failing anyway\"" \
+  "Redefine -pycmd MI command from Python MI command"
+
+mi_gdb_test "-pycmd int" \
+  "\\^done,result=\"42\"" \
+  "-pycmd int"
+
+mi_gdb_test "python pycmd1('')" \
+  ".*\\^error,msg=\"MI command name is empty.\"" \
+  "empty MI command name"
+
+mi_gdb_test "python pycmd1('-')" \
+  ".*\\^error,msg=\"MI command name does not start with '-' followed by at least one letter or digit.\"" \
+  "invalid MI command name"
+
+mi_gdb_test "python pycmd1('-bad-character-@')" \
+  ".*\\^error,msg=\"MI command name contains invalid character: @.\"" \
+  "invalid character in MI command name"
diff --git a/gdb/testsuite/gdb.python/py-mi-cmd.py b/gdb/testsuite/gdb.python/py-mi-cmd.py
new file mode 100644
index 0000000000..24aa475095
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-mi-cmd.py
@@ -0,0 +1,54 @@
+import gdb
+
+class BadKey:
+    def __repr__(self):
+        return "Bad Kay"
+
+class ReallyBadKey:
+    def __repr__(self):
+        return BadKey()
+
+
+class pycmd1(gdb.MICommand):
+    def invoke(self, argv):
+        if argv[0] == 'int':
+            return 42
+        elif argv[0] == 'str':
+            return "Hello world!"
+        elif argv[0] == 'ary':
+            return [ 'Hello', 42 ]
+        elif argv[0] == "dct":
+            return { 'hello' : 'world', 'times' : 42}
+        elif argv[0] == "bk1":
+            return { BadKey() : 'world' }
+        elif argv[0] == "bk2":
+            return { 1 : 'world' }
+        elif argv[0] == "bk3":
+            return { ReallyBadKey() : 'world' }
+        elif argv[0] == 'tpl':
+            return ( 42 , 'Hello' )
+        elif argv[0] == 'itr':
+            return iter([1,2,3])
+        elif argv[0] == 'nn1':
+            return None
+        elif argv[0] == 'nn2':
+            return [ None ]
+        elif argv[0] == 'red':
+            pycmd2('-pycmd')
+            return None
+        elif argv[0] == 'exp':
+            raise gdb.GdbError()
+        else:
+            raise gdb.GdbError("Invalid parameter: %s" % argv[0])
+
+
+class pycmd2(gdb.MICommand):
+    def invoke(self, argv):
+        if argv[0] == 'str':
+            return "Ciao!"
+        elif argv[0] == 'red':
+            pycmd1('-pycmd')
+            raise gdb.GdbError("Command redefined but we failing anyway")
+        else:
+            raise gdb.GdbError("Invalid parameter: %s" % argv[0])
+
-- 
2.20.1

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH v3 0/5] Create MI commands using python.
  2019-05-30 13:49     ` [PATCH v3 0/5] Create MI commands using python Jan Vrany
@ 2019-06-10 12:20       ` Jan Vrany
  0 siblings, 0 replies; 50+ messages in thread
From: Jan Vrany @ 2019-06-10 12:20 UTC (permalink / raw)
  To: gdb-patches

Polite ping. 

Thanks! Jan

On Thu, 2019-05-30 at 14:48 +0100, Jan Vrany wrote:
> This version mainly addresses comments raised by Simon. Tested with both
> Python 2.7 and Python 3(.7).
> 
> Changes v2 -> v3
> 
> * addressed Simon's comments:
>   - https://sourceware.org/ml/gdb-patches/2019-05/msg00406.html
>   - https://sourceware.org/ml/gdb-patches/2019-05/msg00407.html
> * checked that it compiles with both Python 2.7 and Python 3(.7)
> * add tests for MI command returning dictionary with non-string
>   keys
> * add tests for creating MI commands with invalid names
> 
> Changes v1 -> v2
> 
> * squashed following commits into one:
>   - 8e65585bdab0 Create MI commands using python.
>   - 9e2afadd2d4d03 mi/python: C++ify python MI command handling code
>   - 0491a0634f7de4 mi/python: Polish MI output of python commands
> * moved class mi_command_py to python code
> * use gdbpy_ref<> instead of void* to hold on Python object
>   in mi_command_py
> * release python objects held on by mi_command_py objects
>   when finalizing Python
> * merged py_mi_invoke() into mi_command_py::invoke()
> * added missing copyright headers
> * dropped micmdpy_parse_command_name()
> * prefixed error messages by MI command name except for
>   "Problem parsing arguments:" since old C-implemented MI
>   commands do not prefix command name.
> * make do_suppress_notification() to return gdb::optional
> * split invoke to invoke and do_invoke, making invoke to perform
>   common initialization
> * add test redefining Python MI command while the very same command
>   is running
> * documented the fact that in mi_command_py::do_invoke() `this` pointer cannot
>   be used after invoking python code since it may got stale.
> 
> 
> Didier Nadeau (3):
>   Use std::map for MI commands in mi-cmds.c
>   Use classes to represent MI Command instead of structures
>   Create MI commands using python.
> 
> Jan Vrany (2):
>   mi/python: Allow redefinition of python MI commands
>   mi/python: Add tests for python-defined MI commands
> 
>  gdb/ChangeLog                          |  88 +++++
>  gdb/Makefile.in                        |   1 +
>  gdb/mi/mi-cmd-info.c                   |   4 +-
>  gdb/mi/mi-cmds.c                       | 510 +++++++++++++------------
>  gdb/mi/mi-cmds.h                       |  87 ++++-
>  gdb/mi/mi-main.c                       |  22 +-
>  gdb/mi/mi-main.h                       |   1 +
>  gdb/mi/mi-parse.c                      |  20 +-
>  gdb/mi/mi-parse.h                      |   6 +-
>  gdb/python/py-micmd.c                  | 307 +++++++++++++++
>  gdb/python/py-micmd.h                  |  60 +++
>  gdb/python/python-internal.h           |   2 +
>  gdb/python/python.c                    |  13 +-
>  gdb/testsuite/ChangeLog                |   5 +
>  gdb/testsuite/gdb.python/py-mi-cmd.exp | 132 +++++++
>  gdb/testsuite/gdb.python/py-mi-cmd.py  |  54 +++
>  16 files changed, 1007 insertions(+), 305 deletions(-)
>  create mode 100644 gdb/python/py-micmd.c
>  create mode 100644 gdb/python/py-micmd.h
>  create mode 100644 gdb/testsuite/gdb.python/py-mi-cmd.exp
>  create mode 100644 gdb/testsuite/gdb.python/py-mi-cmd.py
> 

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH v3 2/5] Use classes to represent MI Command instead of structures
  2019-05-30 13:49     ` [PATCH v3 2/5] Use classes to represent MI Command instead of structures Jan Vrany
@ 2019-06-18 19:38       ` Pedro Alves
  0 siblings, 0 replies; 50+ messages in thread
From: Pedro Alves @ 2019-06-18 19:38 UTC (permalink / raw)
  To: Jan Vrany, gdb-patches; +Cc: Didier Nadeau

On 5/30/19 2:48 PM, Jan Vrany wrote:
> From: Didier Nadeau <didier.nadeau@gmail.com>
> 
> This commit changes the infrastructure of mi-cmds.h and associated
> files to use classes instead of structure to populate the hashmap
> containing the commands.
> 
> The base class is virtual and there is one subclass MI commands
> implemented with pure MI implementation and another for MI commands
> implemented over a CLI command.
> 
> Logic for suppress_notification and parsing of ARGV/ARGC has been moved
> to the classes implementation.
> 
> gdb/ChangeLog:
> 
> 	* mi/mi-cmds.c (create_mi_cmd): Remove.
> 	(mi_command::mi_command): New function.
> 	(mi_command::do_suppress_notification): New function.
> 	(mi_command::invoke): New function.
> 	(mi_command_mi::mi_command_mi): New function.
> 	(mi_command_mi::do_invoke): New function.
> 	(mi_command_cli::mi_command_cli): New function.
> 	(mi_command_cli::do_invoke): New function.
> 	(mi_cmd_lookup): Change return type.
> 	* mi/mi-cmds.h (struct mi_cli): Remove.
> 	(struct mi_cmd): Remove.
> 	(class mi_command): New class.
> 	(class mi_command_mi): New class.
> 	(class mi_command_cli): New class.
> 	(mi_cmd_loopkup): Change return type.
> 	* mi/mi-main.c (mi_execute_cli_command): Remove declaration.
> 	(mi_execute_command): Remove suppress_notification handling.
> 	(mi_cmd_execute): Remove call to argv_func.
> 	(mi_cmd_execute): Remove call to mi_execute_cli_command.
> 	(mi_cmd_execute): New call to mi_command::invoke.
> 	* mi/mi-main.h (mi_execute_cli_command): New declaration.
> 	* mi/mi-parse.c (mi_parse): Remove call to mi_parse_argv.
> 	* mi/mi-parse.h (struct mi_parse): Remove field struct mi_cmd.
> 	(struct mi_parse): New field class mi_command.
> 	(mi_parse_argv): New declaration.
> ---
>  gdb/ChangeLog        |  29 +++++++++++++
>  gdb/mi/mi-cmd-info.c |   2 +-
>  gdb/mi/mi-cmds.c     | 100 ++++++++++++++++++++++++++++++-------------
>  gdb/mi/mi-cmds.h     |  75 ++++++++++++++++++++++----------
>  gdb/mi/mi-main.c     |  22 +---------
>  gdb/mi/mi-main.h     |   1 +
>  gdb/mi/mi-parse.c    |  18 ++------
>  gdb/mi/mi-parse.h    |   6 ++-
>  8 files changed, 164 insertions(+), 89 deletions(-)
> 
> diff --git a/gdb/ChangeLog b/gdb/ChangeLog
> index 8e964c0adb..994ad947bd 100644
> --- a/gdb/ChangeLog
> +++ b/gdb/ChangeLog
> @@ -1,3 +1,32 @@
> +2019-05-02  Didier Nadeau  <didier.nadeau@gmail.com>
> +	    Jan Vrany <jan.vrany@fit.cvut.cz>
> +
> +	* mi/mi-cmds.c (create_mi_cmd): Remove.
> +	(mi_command::mi_command): New function.
> +	(mi_command::do_suppress_notification): New function.
> +	(mi_command::invoke): New function.
> +	(mi_command_mi::mi_command_mi): New function.
> +	(mi_command_mi::do_invoke): New function.
> +	(mi_command_cli::mi_command_cli): New function.
> +	(mi_command_cli::do_invoke): New function.
> +	(mi_cmd_lookup): Change return type.
> +	* mi/mi-cmds.h (struct mi_cli): Remove.
> +	(struct mi_cmd): Remove.
> +	(class mi_command): New class.
> +	(class mi_command_mi): New class.
> +	(class mi_command_cli): New class.
> +	(mi_cmd_loopkup): Change return type.
> +	* mi/mi-main.c (mi_execute_cli_command): Remove declaration.
> +	(mi_execute_command): Remove suppress_notification handling.
> +	(mi_cmd_execute): Remove call to argv_func.
> +	(mi_cmd_execute): Remove call to mi_execute_cli_command.
> +	(mi_cmd_execute): New call to mi_command::invoke.
> +	* mi/mi-main.h (mi_execute_cli_command): New declaration.
> +	* mi/mi-parse.c (mi_parse): Remove call to mi_parse_argv.
> +	* mi/mi-parse.h (struct mi_parse): Remove field struct mi_cmd.
> +	(struct mi_parse): New field class mi_command.
> +	(mi_parse_argv): New declaration.
> +
>  2019-05-02  Didier Nadeau  <didier.nadeau@gmail.com>
>              Jan Vrany <jan.vrany@fit.cvut.cz>
>  
> diff --git a/gdb/mi/mi-cmd-info.c b/gdb/mi/mi-cmd-info.c
> index 8645fac294..3d8bd68264 100644
> --- a/gdb/mi/mi-cmd-info.c
> +++ b/gdb/mi/mi-cmd-info.c
> @@ -67,7 +67,7 @@ void
>  mi_cmd_info_gdb_mi_command (const char *command, char **argv, int argc)
>  {
>    const char *cmd_name;
> -  struct mi_cmd *cmd;
> +  mi_command *cmd;
>    struct ui_out *uiout = current_uiout;
>  
>    /* This command takes exactly one argument.  */
> diff --git a/gdb/mi/mi-cmds.c b/gdb/mi/mi-cmds.c
> index 13db22afaf..2d8863c5f9 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 <map>
>  #include <string>
>  
> @@ -36,9 +37,9 @@ static bool
>  insert_mi_cmd_entry (mi_cmd_up command)
>  {
>    gdb_assert (command != NULL);
> -  gdb_assert (command->name != NULL);
> +  gdb_assert (!command->name ().empty ());
>  
> -  std::string name (command->name);
> +  const std::string &name = command->name ();
>  
>    if (mi_cmd_table.find (name) != mi_cmd_table.end ())
>      return false;
> @@ -48,32 +49,16 @@ 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 a pure MI implementation.  */
>  
>  static void
>  add_mi_cmd_mi (const char *name, mi_cmd_argv_ftype function,
>  	       int *suppress_notification = NULL)
>  {
> -  mi_cmd_up cmd_up = create_mi_cmd (name);
> -
> -  cmd_up->cli.cmd = NULL;
> -  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);
>  }
>  
> @@ -83,17 +68,72 @@ static void
>  add_mi_cmd_cli (const char *name, const char *cli_name, int args_p,
>  		int *suppress_notification = NULL)
>  {
> -  mi_cmd_up cmd_up = create_mi_cmd (name);
> +  mi_cmd_up command (new mi_command_cli (name, cli_name, args_p,
> +                                         suppress_notification));
>  
> -  cmd_up->cli.cmd = cli_name;
> -  cmd_up->cli.args_p = args_p;
> -  cmd_up->argv_func = NULL;
> -  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);
>  }
>  
> +/* See mi-cmds.h  */

Missing period, and missing empty line between the comment and
the function.

> +mi_command::mi_command (const char *name, int *suppress_notification)
> +  : m_name (name),
> +    m_suppress_notification (suppress_notification)
> +{}
> +
> +
> +void
> +mi_command::invoke (struct mi_parse *parse)
> +{
> +  gdb::optional<scoped_restore_tmpl<int>> restore
> +    = do_suppress_notification ();
> +  this->do_invoke (parse);
> +}
> +
> +gdb::optional<scoped_restore_tmpl<int>>
> +mi_command::do_suppress_notification ()
> +{
> +  if (m_suppress_notification != NULL)
> +    return scoped_restore_tmpl<int> (m_suppress_notification, 1);
> +  else
> +    return {};
> +}
> +
> +mi_command_mi::mi_command_mi (const char *name, mi_cmd_argv_ftype func,
> +			      int *suppress_notification)
> +  : mi_command (name, suppress_notification),
> +    m_argv_function (func)
> +{
> +  gdb_assert (func != NULL);
> +}
> +
> +void
> +mi_command_mi::do_invoke (struct mi_parse *parse)
> +{
> +  mi_parse_argv (parse->args, parse);
> +
> +  if (parse->argv == NULL)
> +    error (_("Problem parsing arguments: %s %s"), parse->command, parse->args);
> +
> +  this->m_argv_function (parse->command, parse->argv, parse->argc);
> +}
> +
> +mi_command_cli::mi_command_cli (const char *name, const char *cli_name,
> +				int args_p, int *suppress_notification)
> +  : mi_command (name, suppress_notification),
> +    m_cli_name (cli_name),
> +    m_args_p (args_p)
> +{}
> +
> +void
> +mi_command_cli::do_invoke (struct mi_parse *parse)
> +{
> +  mi_execute_cli_command (this->m_cli_name.c_str (), this->m_args_p,
> +			  parse->args);
> +}
> +
> +/* Initialize the available MI commands.  */
> +
>  static void
>  build_table ()
>  {
> @@ -126,7 +166,7 @@ build_table ()
>    add_mi_cmd_mi ("catch-exception", mi_cmd_catch_exception,
>  		 &mi_suppress_notification.breakpoint);
>    add_mi_cmd_mi ("catch-handlers", mi_cmd_catch_handlers,
> -		 &mi_suppress_notification.breakpoint);		 
> +		 &mi_suppress_notification.breakpoint);

You should fix this whitespace issue in patch #1, i.e., make
patch #1 not introduce the problem in the first place.

>    add_mi_cmd_mi ("catch-load", mi_cmd_catch_load,
>  		 &mi_suppress_notification.breakpoint);
>    add_mi_cmd_mi ("catch-unload", mi_cmd_catch_unload,
> @@ -244,7 +284,7 @@ build_table ()
>  
>  /* See mi-cmds.h.  */
>  
> -struct mi_cmd *
> +mi_command *
>  mi_cmd_lookup (const char *command)
>  {
>    gdb_assert (command != NULL);
> diff --git a/gdb/mi/mi-cmds.h b/gdb/mi/mi-cmds.h
> index becd788d0f..f1b4728bde 100644
> --- a/gdb/mi/mi-cmds.h
> +++ b/gdb/mi/mi-cmds.h
> @@ -127,38 +127,69 @@ 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.  */
> +/* mi_command base virtual class.  */

Say "abstract base class" instead of "base virtual class".

"virtual class" has a different meaning in C++, it refers
to virtual inheritance, which you're not using here.

How about:

/* Abstract base class inherited by MI commands with
   either a pure MI implementation or implemented on
   top of CLI commands.  */

>  
> -struct mi_cli
> +class mi_command
>  {
> -  /* 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;
> +public:
> +  mi_command (const char *name, int *suppress_notification);
> +  virtual ~mi_command () {};

Spurious ";" after the dtor's body.

> +
> +  const std::string &name ()
> +  { return m_name; }
> +
> +  /* Execute the MI command.  */
> +  void invoke (struct mi_parse *parse);
> +
> +protected:
> +  gdb::optional<scoped_restore_tmpl<int>> do_suppress_notification ();
> +  virtual void do_invoke(struct mi_parse *parse) = 0;

These two methods should be documented here.

> +
> +private:
> +
> +  /* The name of the command.  */
> +  std::string m_name;
> +
> +  /* Pointer to integer to set during command's invocation.  */
> +  int *m_suppress_notification;
>  };
>  
> -struct mi_cmd
> +/* MI command with a pure MI implementation.  */
> +
> +class mi_command_mi : public mi_command
>  {
> -  /* 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;
> +public:
> +  mi_command_mi (const char *name, mi_cmd_argv_ftype func,
> +                 int *suppress_notification);

The ctor's arguments should be documented.  Particularly,
what's FUNC ?

> +
> +protected:
> +  void do_invoke(struct mi_parse *parse) override;

Missing space before open parens.

> +
> +private:
> +  mi_cmd_argv_ftype *m_argv_function;

Please document this.

> +};
> +
> +/* MI command implemented on top of a CLI command.  */
> +
> +class mi_command_cli : public mi_command
> +{
> +public:
> +  mi_command_cli (const char *name, const char *cli_name, int args_p,
> +                  int *suppress_notification);
> +
> +protected:
> +  void do_invoke(struct mi_parse *parse) override;

Missing space.

> +
> +private:
> +  std::string m_cli_name;
> +  int m_args_p;

Please document these.  

Some of these fields were documented before but it seems like
you're just dropping the comments.  E.g., we can see this talking
about args_p in the context above:

 > -  /* 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;

Please make sure that we're not losing such comments.


>  };
>  
> -typedef std::unique_ptr<struct mi_cmd> mi_cmd_up;
> +typedef std::unique_ptr<mi_command> mi_cmd_up;
>  
>  /* Lookup a command in the MI command table.  */
>  
> -extern struct mi_cmd *mi_cmd_lookup (const char *command);
> +extern mi_command *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 4921c13528..921d0b5a03 100644
> --- a/gdb/mi/mi-main.c
> +++ b/gdb/mi/mi-main.c
> @@ -90,9 +90,6 @@ int running_result_record_printed = 1;
>  int mi_proceeded;
>  
>  static void mi_cmd_execute (struct mi_parse *parse);
> -
> -static void mi_execute_cli_command (const char *cmd, int 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 *,
> @@ -1952,11 +1949,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)
> @@ -2095,18 +2087,8 @@ 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.  */
> -      mi_execute_cli_command (parse->cmd->cli.cmd, parse->cmd->cli.args_p,
> -			      parse->args);
> -    }
> +  if (parse->cmd != NULL)
> +    parse->cmd->invoke (parse);
>    else
>      {
>        /* FIXME: DELETE THIS.  */
> diff --git a/gdb/mi/mi-main.h b/gdb/mi/mi-main.h
> index 1986228d22..90d54a6eed 100644
> --- a/gdb/mi/mi-main.h
> +++ b/gdb/mi/mi-main.h
> @@ -54,6 +54,7 @@ struct mi_suppress_notification
>  };
>  extern struct mi_suppress_notification mi_suppress_notification;
>  
> +void mi_execute_cli_command (const char *cmd, int args_p, const char *args);
>  /* Implementation of -fix-multi-location-breakpoint-output.  */

Write "extern void" ... like the other declarations around this.

Also, please add an empty line between the new declaration and the
unrelated comment just below.

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH v3 3/5] Create MI commands using python.
  2019-05-30 13:49     ` [PATCH v3 3/5] Create MI commands using python Jan Vrany
@ 2019-06-18 19:43       ` Pedro Alves
  0 siblings, 0 replies; 50+ messages in thread
From: Pedro Alves @ 2019-06-18 19:43 UTC (permalink / raw)
  To: Jan Vrany, gdb-patches; +Cc: Didier Nadeau

On 5/30/19 2:48 PM, Jan Vrany wrote:
> From: Didier Nadeau <didier.nadeau@gmail.com>
> 
> This commit allows an user to create custom MI commands using Python
> similarly to what is possible for Python CLI commands.
> 
> A new subclass of mi_command is defined for Python MI commands,
> mi_command_py. A new file, py-micmd.c contains the logic for Python
> MI commands.
> 
> gdb/ChangeLog
> 

>     * python/py-micmd.h: New file
>     (micmdpy_object): New struct.
>     (micmdpy_object): New typedef.

This typedef shouldn't be necessary?

>     (mi_command_py): New class.
>     * python/python-internal.h
>     (gdbpy_initialize_micommands): New declaration.
>     * python/python.c
>     (_initialize_python): New call to gdbpy_initialize_micommands.
>     (finalize_python): Finalize Python MI commands.
> ---
>  gdb/ChangeLog                |  23 +++
>  gdb/Makefile.in              |   1 +
>  gdb/mi/mi-cmds.c             |   7 +-
>  gdb/mi/mi-cmds.h             |   9 ++
>  gdb/python/py-micmd.c        | 297 +++++++++++++++++++++++++++++++++++
>  gdb/python/py-micmd.h        |  60 +++++++
>  gdb/python/python-internal.h |   2 +
>  gdb/python/python.c          |  13 +-
>  8 files changed, 407 insertions(+), 5 deletions(-)
>  create mode 100644 gdb/python/py-micmd.c
>  create mode 100644 gdb/python/py-micmd.h
> 
> diff --git a/gdb/ChangeLog b/gdb/ChangeLog
> index 994ad947bd..241e5da68f 100644
> --- a/gdb/ChangeLog
> +++ b/gdb/ChangeLog
> @@ -1,3 +1,26 @@
> +2019-05-02  Didier Nadeau  <didier.nadeau@gmail.com>
> +            Jan Vrany  <jan.vrany@fit.cvut.cz>
> +
> +	* Makefile.in (SUBDIR_PYTHON_SRCS): Add py-micmd.c.
> +	* mi/mi-cmds.c (insert_mi_cmd_entry): Remove static.
> +	(mi_cmd_table): Remove static.
> +	* mi/mi-cmds.h (insert_mi_cmd_entry): New declaration.
> +	(mi_cmd_table): New declaration.
> +	* python/py-micmd.c: New file
> +	(parse_mi_result): New function.
> +	(micmdpy_init): New function.
> +	(gdbpy_initialize_micommands): New function.
> +	(mi_command_py): New class.
> +	* python/py-micmd.h: New file
> +	(micmdpy_object): New struct.
> +	(micmdpy_object): New typedef.
> +	(mi_command_py): New class.
> +	* python/python-internal.h
> +	(gdbpy_initialize_micommands): New declaration.
> +	* python/python.c
> +	(_initialize_python): New call to gdbpy_initialize_micommands.
> +	(finalize_python): Finalize Python MI commands.
> +
>  2019-05-02  Didier Nadeau  <didier.nadeau@gmail.com>
>  	    Jan Vrany <jan.vrany@fit.cvut.cz>
>  
> diff --git a/gdb/Makefile.in b/gdb/Makefile.in
> index 0f49578360..28866962bc 100644
> --- a/gdb/Makefile.in
> +++ b/gdb/Makefile.in
> @@ -382,6 +382,7 @@ SUBDIR_PYTHON_SRCS = \
>  	python/py-instruction.c \
>  	python/py-lazy-string.c \
>  	python/py-linetable.c \
> +	python/py-micmd.c \
>  	python/py-newobjfileevent.c \
>  	python/py-objfile.c \
>  	python/py-param.c \
> diff --git a/gdb/mi/mi-cmds.c b/gdb/mi/mi-cmds.c
> index 2d8863c5f9..0aead954f9 100644
> --- a/gdb/mi/mi-cmds.c
> +++ b/gdb/mi/mi-cmds.c
> @@ -28,12 +28,11 @@
>  
>  /* MI command table (built at run time). */
>  
> -static std::map<std::string, mi_cmd_up> mi_cmd_table;
> +std::map<std::string, mi_cmd_up> mi_cmd_table;
>  
> -/* Insert a new mi-command into the command table.  Return true if
> -   insertion was successful.  */
> +/* See mi-cmds.h.  */
>  
> -static bool
> +bool
>  insert_mi_cmd_entry (mi_cmd_up command)
>  {
>    gdb_assert (command != NULL);
> diff --git a/gdb/mi/mi-cmds.h b/gdb/mi/mi-cmds.h
> index f1b4728bde..5ca7232fca 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 <map>

Include <string> as well.

> +
>  enum print_values {
>     PRINT_NO_VALUES,
>     PRINT_ALL_VALUES,
> @@ -191,9 +193,16 @@ typedef std::unique_ptr<mi_command> mi_cmd_up;
>  
>  extern mi_command *mi_cmd_lookup (const char *command);
>  
> +extern std::map<std::string, mi_cmd_up> mi_cmd_table;
> +
>  /* Debug flag */
>  extern int mi_debug_p;
>  
>  extern void mi_execute_command (const char *cmd, int from_tty);
>  
> +/* Insert a new mi-command into the command table.  Return true if
> +   insertion was successful.  */
> +
> +extern bool insert_mi_cmd_entry (mi_cmd_up command);

Use "mi_cmd_up &&command" ?

> +
>  #endif /* MI_MI_CMDS_H */
> diff --git a/gdb/python/py-micmd.c b/gdb/python/py-micmd.c
> new file mode 100644
> index 0000000000..e99632c97a
> --- /dev/null
> +++ b/gdb/python/py-micmd.c
> @@ -0,0 +1,297 @@
> +/* MI Command Set for GDB, the GNU debugger.
> +
> +   Copyright (C) 2019 Free Software Foundation, Inc.

If any of this code was posted in previous years, please write
201x-2019 for the year range (with x replaced with the proper
digit, of course).  Likewise all other files in the patch.

> +
> +   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/>.  */
> +
> +/* gdb MI commands implemented in Python  */
> +
> +#include "defs.h"
> +#include "python-internal.h"
> +#include "python/py-micmd.h"
> +#include "arch-utils.h"
> +#include "charset.h"
> +#include "language.h"
> +
> +#include <string>
> +
> +static PyObject *invoke_cst;

Document.

> +
> +extern PyTypeObject
> +  micmdpy_object_type CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("micmdpy_object");

Is there a reason this is "extern"?  I don't see this used anywhere
outside py-micmd.c.


> +
> +/* If the command invoked returns a list, this function parses it and create an
> +   appropriate MI out output.
> +
> +   The returned values must be Python string,

"values ... string" doesn't parse correctly for me.

Did you mean

   "The returned value must be a Python string

?

>  and can be contained within Python
> +   lists and dictionaries. 

I think I'm a bit more confused now.  :-)

Can you clarify this please?

> +   lists and dictionaries. It is possible to have a multiple levels of lists

Double space after periods.

"a multiple levels" => "multiple levels" 


> +   and/or dictionaries.  */
> +
> +static void
> +parse_mi_result (PyObject *result, const char *field_name)
> +{
> +  struct ui_out *uiout = current_uiout;
> +
> +  if (PyDict_Check (result))
> +    {
> +      PyObject *key, *value;
> +      Py_ssize_t pos = 0;
> +      ui_out_emit_tuple tuple_emitter (uiout, field_name);
> +      while (PyDict_Next (result, &pos, &key, &value))
> +	{
> +	  if (!PyString_Check (key))
> +	    {
> +	      gdbpy_ref<> key_repr (PyObject_Repr (key));
> +	      if (PyErr_Occurred () != NULL)
> +                {
> +                  gdbpy_err_fetch ex;
> +                  gdb::unique_xmalloc_ptr<char> ex_msg (ex.to_string ());
> +
> +                  if (ex_msg == NULL || *ex_msg == '\0')
> +                    error (_("Non-string object used as key."));
> +                  else
> +                    error (_("Non-string object used as key: %s."),
> +                           ex_msg.get ());
> +                }
> +              else
> +	        {
> +	          auto key_repr_string
> +	                 = python_string_to_target_string (key_repr.get ());
> +	          error (_("Non-string object used as key: %s."),
> +                         key_repr_string.get ());
> +	        }
> +	    }

There's aspace vs tabs mixup above, and in other parts
of the file too.  Please fix that throughout.

> +
> +	  auto key_string = python_string_to_target_string (key);
> +	  parse_mi_result (value, key_string.get ());
> +	}
> +    }
> +  else if (PySequence_Check (result) && !PyString_Check (result))
> +    {
> +      ui_out_emit_list list_emitter (uiout, field_name);
> +      for (Py_ssize_t i = 0; i < PySequence_Size (result); ++i)
> +	{
> +          gdbpy_ref<> item (PySequence_ITEM (result, i));
> +	  parse_mi_result (item.get (), NULL);
> +	}
> +    }
> +  else if (PyIter_Check (result))
> +    {
> +      gdbpy_ref<> item;
> +      ui_out_emit_list list_emitter (uiout, field_name);
> +      while (item.reset (PyIter_Next (result)), item != nullptr)
> +	parse_mi_result (item.get (), NULL);
> +    }
> +  else
> +    {
> +      gdb::unique_xmalloc_ptr<char> string (gdbpy_obj_to_string (result));
> +      uiout->field_string (field_name, string.get ());
> +    }
> +}
> +
> +/* Object initializer; sets up gdb-side structures for MI command.

"for an MI command", I suppose?

> +
> +   Use: __init__(NAME).

Did you mean "Usage:"?  Or that this function implements
__init__?

> +
> +   NAME is the name of the MI command to register.  It must start with a dash
> +   as traditional MI commands do.  */
> +
> +static int
> +micmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
> +{
> +  const char *name;
> +  gdbpy_ref<> self_ref = gdbpy_ref<>::new_reference (self);
> +
> +  if (!PyArg_ParseTuple (args, "s", &name))
> +    return -1;
> +
> +  /* Validate command name */

Period and double space.

> +  const int name_len = strlen (name);
> +  if (name_len == 0)
> +    {
> +      error (_("MI command name is empty."));
> +      return -1;

the return line is unreacheable, since error throws.
Remove it, and then remove the unnecessary braces.

> +    }
> +  else if ((name_len < 2) || (name[0] != '-') || !isalnum (name[1]))

Please remove reduntant parens:

    else if (name_len < 2 || name[0] != '-' || !isalnum (name[1]))

> +    {
> +      error (_("MI command name does not start with '-'"
> +               " followed by at least one letter or digit."));
> +      return -1;

Likewise remove the return line.

> +    }
> +  else

Since the conditions above if reached never return you
could drop the "else"s, which removes this indentation level.
Also, I'd use "== 1" instead of "< 2", since you've already
checked 0.

Like:

  if (name_len == 0)
    error (_("MI command name is empty."));

  if (name_len == 1 || name[0] != '-' || !isalnum name[1])
    error (_("MI command name does not start with '-'"
             " followed by at least one letter or digit."));

  for (int i = 2; i < name_len; i++)
    {
  ...

> +    for (int i = 2; i < name_len; i++)
> +      {
> +	if (!isalnum (name[i]) && name[i] != '-')
> +	  {
> +	    error (_("MI command name contains invalid character: %c."),
> +                   name[i]);
> +	    return -1;

Ditto re. the return after error.

> +	  }
> +      }
> +
> +  if (!PyObject_HasAttr (self_ref.get (), invoke_cst))
> +      error (_("-%s: Python command object missing 'invoke' method."), name);

Indentation of "error" doesn't look right.

> +
> +  try
> +    {
> +      mi_cmd_up micommand = mi_cmd_up(new mi_command_py (name + 1, self_ref));
> +
> +      bool result = insert_mi_cmd_entry (std::move (micommand));
> +
> +      if (!result)
> +	{
> +	  error (_("Unable to insert command."
> +                   "The name might already be in use."));

Indentation isn't right here.

> +	  return -1;

Remove, unreachable.

> +	}
> +    }
> +  catch (const gdb_exception &except)
> +    {
> +      GDB_PY_SET_HANDLE_EXCEPTION (except);
> +    }
> +
> +  return 0;
> +}
> +
> +mi_command_py::mi_command_py (const char *name, gdbpy_ref<> object)
> +  : mi_command (name, NULL), pyobj (object)
> +{
> +}
> +
> +void
> +mi_command_py::do_invoke (struct mi_parse *parse)
> +{
> +  mi_parse_argv (parse->args, parse);
> +
> +  if (parse->argv == NULL)
> +    error (_("Problem parsing arguments: %s %s"), parse->command, parse->args);
> +
> +  PyObject *obj = this->pyobj.get ();
> +
> +  gdbpy_enter enter_py (get_current_arch (), current_language);
> +
> +  gdb_assert (obj != nullptr);
> +
> +  if (!PyObject_HasAttr (obj, invoke_cst))
> +      error (_("-%s: Python command object missing 'invoke' method."),
> +	     name ().c_str ());
> +
> +
> +  gdbpy_ref<> argobj (PyList_New (parse->argc));
> +  if (argobj == nullptr)
> +    {
> +      gdbpy_print_stack ();
> +      error (_("-%s: failed to create the Python arguments list."),
> +	     name ().c_str ());
> +    }
> +
> +  for (int i = 0; i < parse->argc; ++i)
> +    {
> +      gdbpy_ref<> str (PyUnicode_Decode (parse->argv[i], strlen (parse->argv[i]),
> +					 host_charset (), NULL));
> +      if (PyList_SetItem (argobj.get (), i, str.release ()) != 0)
> +	{
> +	  error (_("-%s: failed to create the Python arguments list."),
> +		 name ().c_str ());
> +	}
> +    }
> +
> +  gdb_assert (PyErr_Occurred () == NULL);
> +  gdbpy_ref<> result (
> +    PyObject_CallMethodObjArgs (obj, invoke_cst, argobj.get (), NULL));
> +  if (PyErr_Occurred () != NULL)
> +    {
> +      gdbpy_err_fetch ex;
> +      gdb::unique_xmalloc_ptr<char> ex_msg (ex.to_string ());
> +
> +      if (ex_msg == NULL || *ex_msg == '\0')
> +	error (_("-%s: failed to execute command"), name ().c_str ());
> +      else
> +	error (_("-%s: %s"), name ().c_str (), ex_msg.get ());
> +    }
> +  else
> +    {
> +      if (Py_None != result)
> +	parse_mi_result (result.get (), "result");
> +    }
> +}
> +
> +void mi_command_py::finalize ()

Line break after void.

> +{
> +  this->pyobj.reset (nullptr);
> +}
> +
> +/* Initialize the MI command object.  */
> +
> +int
> +gdbpy_initialize_micommands ()
> +{
> +  micmdpy_object_type.tp_new = PyType_GenericNew;
> +  if (PyType_Ready (&micmdpy_object_type) < 0)
> +    return -1;
> +
> +  if (gdb_pymodule_addobject (gdb_module, "MICommand",
> +			      (PyObject *) &micmdpy_object_type)
> +      < 0)
> +    return -1;
> +
> +  invoke_cst = PyString_FromString ("invoke");
> +  if (invoke_cst == NULL)
> +    return -1;
> +
> +  return 0;
> +}
> +
> +static PyMethodDef micmdpy_object_methods[] = {{0}};
> +
> +PyTypeObject micmdpy_object_type = {

Can this be static?

> +  PyVarObject_HEAD_INIT (NULL, 0) "gdb.MICommand", /*tp_name */

Line break between the PyVarObject_HEAD_INIT statement
and "gdb.MICommand".


> +  sizeof (micmdpy_object),			   /*tp_basicsize */
> +  0,						   /*tp_itemsize */
> +  0,						   /*tp_dealloc */
> +  0,						   /*tp_print */
> +  0,						   /*tp_getattr */
> +  0,						   /*tp_setattr */
> +  0,						   /*tp_compare */
> +  0,						   /*tp_repr */
> +  0,						   /*tp_as_number */
> +  0,						   /*tp_as_sequence */
> +  0,						   /*tp_as_mapping */
> +  0,						   /*tp_hash */
> +  0,						   /*tp_call */
> +  0,						   /*tp_str */
> +  0,						   /*tp_getattro */
> +  0,						   /*tp_setattro */
> +  0,						   /*tp_as_buffer */
> +  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,	/*tp_flags */
> +  "GDB mi-command object",			   /* tp_doc */
> +  0,						   /* tp_traverse */
> +  0,						   /* tp_clear */
> +  0,						   /* tp_richcompare */
> +  0,						   /* tp_weaklistoffset */
> +  0,						   /* tp_iter */
> +  0,						   /* tp_iternext */
> +  micmdpy_object_methods,			   /* tp_methods */
> +  0,						   /* tp_members */
> +  0,						   /* tp_getset */
> +  0,						   /* tp_base */
> +  0,						   /* tp_dict */
> +  0,						   /* tp_descr_get */
> +  0,						   /* tp_descr_set */
> +  0,						   /* tp_dictoffset */
> +  micmdpy_init,					   /* tp_init */
> +  0,						   /* tp_alloc */
> +};
> diff --git a/gdb/python/py-micmd.h b/gdb/python/py-micmd.h
> new file mode 100644
> index 0000000000..deadc0116b
> --- /dev/null
> +++ b/gdb/python/py-micmd.h
> @@ -0,0 +1,60 @@
> +/* MI Command Set for GDB, the GNU debugger.
> +
> +   Copyright (C) 2019 Free Software Foundation, Inc.
> +
> +   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/>.  */
> +
> +#ifndef PY_MICMDS_H
> +#define PY_MICMDS_H
> +
> +#include "mi/mi-cmds.h"
> +#include "mi/mi-parse.h"
> +#include "python-internal.h"
> +#include "python/py-ref.h"
> +
> +struct micmdpy_object
> +{
> +  PyObject_HEAD
> +};
> +
> +typedef struct micmdpy_object micmdpy_object;

This typedef shouldn't be needed.

> +
> +/* MI command implemented in Python.  */
> +
> +class mi_command_py : public mi_command
> +{
> +  public:

Indentation of "public" etc. should be under "class".
I think you fixed other files in previous iterations but
this was left behind.

> +    /* Constructs a new mi_command_py object.  NAME is command name without
> +       leading dash.  OBJECT is a reference to a Python object implementing
> +       the command.  This object should inherit from gdb.MICommand and should
> +       implement method invoke (args). */
> +
> +    mi_command_py (const char *name, gdbpy_ref<> object);
> +
> +
> +    /* This is called just before shutting down a Python interpreter
> +       to release python object implementing the command. */

..."to release THE python"...

Double space after period.


> +
> +    void finalize ();
> +
> +  protected:
> +   virtual void do_invoke(struct mi_parse *parse) override;

Missing space before parens.  Drop the "virtual".

> +
> +  private:
> +    gdbpy_ref<> pyobj;

Should be called "m_pyobj".  And should have a describing comment.

> +};
> +
> +#endif
> diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
> index 69ff1fe30d..f28027741e 100644
> --- a/gdb/python/python-internal.h
> +++ b/gdb/python/python-internal.h
> @@ -539,6 +539,8 @@ int gdbpy_initialize_xmethods (void)
>    CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
>  int gdbpy_initialize_unwind (void)
>    CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
> +int gdbpy_initialize_micommands (void)
> +  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
>  
>  /* A wrapper for PyErr_Fetch that handles reference counting for the
>     caller.  */
> diff --git a/gdb/python/python.c b/gdb/python/python.c
> index 4dad8ec10d..901c0a4edc 100644
> --- a/gdb/python/python.c
> +++ b/gdb/python/python.c
> @@ -36,6 +36,8 @@
>  #include <ctype.h>
>  #include "location.h"
>  #include "ser-event.h"
> +#include "mi/mi-cmds.h"
> +#include "py-micmd.h"
>  
>  /* Declared constants and enum for python stack printing.  */
>  static const char python_excp_none[] = "none";
> @@ -1564,6 +1566,14 @@ finalize_python (void *ignore)
>    python_gdbarch = target_gdbarch ();
>    python_language = current_language;
>  
> +  for (const auto& name_and_cmd : mi_cmd_table)

The "&" should be leaning on the right side, like
we format "*":

  const auto &name_and_cmd


> +    {
> +      mi_command *cmd = name_and_cmd.second.get ();
> +      mi_command_py *cmd_py = dynamic_cast<mi_command_py*> (cmd);
> +      if (cmd_py != nullptr)
> +        cmd_py->finalize ();
> +    }
> +
>    Py_Finalize ();
>  
>    restore_active_ext_lang (previous_active);
> @@ -1698,7 +1708,8 @@ do_start_initialization ()
>        || gdbpy_initialize_event () < 0
>        || gdbpy_initialize_arch () < 0
>        || gdbpy_initialize_xmethods () < 0
> -      || gdbpy_initialize_unwind () < 0)
> +      || gdbpy_initialize_unwind () < 0
> +      || gdbpy_initialize_micommands () < 0)
>      return false;
>  
>  #define GDB_PY_DEFINE_EVENT_TYPE(name, py_name, doc, base)	\
> 

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH v3 4/5] mi/python: Allow redefinition of python MI commands
  2019-05-30 13:49     ` [PATCH v3 4/5] mi/python: Allow redefinition of python MI commands Jan Vrany
@ 2019-06-18 20:03       ` Pedro Alves
  0 siblings, 0 replies; 50+ messages in thread
From: Pedro Alves @ 2019-06-18 20:03 UTC (permalink / raw)
  To: Jan Vrany, gdb-patches

On 5/30/19 2:48 PM, Jan Vrany wrote:
> Redefining python MI commands is especially useful when developing
> them.
> 
> gdb/Changelog:
> 
> 	* mi/mi-cmds.h (mi_command::can_be_redefined): New method.
> 	* mi/mi-cmds.c: (mi_command::can_be_redefined): New method.
> 	(insert_mi_cmd_entry): Allow redefinition of python-defined MI commands.
> 	* python/py-micmd.h (mi_command_py::can_be_redefined): New method.
> 	* python/py-micmd.c: (mi_command_py::can_be_redefined): New method.
> ---
>  gdb/ChangeLog         |  8 ++++++++
>  gdb/mi/mi-cmds.c      | 10 +++++++++-
>  gdb/mi/mi-cmds.h      |  3 +++
>  gdb/python/py-micmd.c | 24 +++++++++++++++++-------
>  gdb/python/py-micmd.h |  2 +-
>  5 files changed, 38 insertions(+), 9 deletions(-)
> 
> diff --git a/gdb/ChangeLog b/gdb/ChangeLog
> index 241e5da68f..1d264c44f7 100644
> --- a/gdb/ChangeLog
> +++ b/gdb/ChangeLog
> @@ -1,3 +1,11 @@
> +2019-05-02  Jan Vrany  <jan.vrany@fit.cvut.cz>
> +
> +	* mi/mi-cmds.h (mi_command::can_be_redefined): New method.
> +	* mi/mi-cmds.c: (mi_command::can_be_redefined): New method.

No ":" after the file name and before "(".

> +	(insert_mi_cmd_entry): Allow redefinition of python-defined MI commands.
> +	* python/py-micmd.h (mi_command_py::can_be_redefined): New method.
> +	* python/py-micmd.c: (mi_command_py::can_be_redefined): New method.

Ditto.

Please double check the other ChangeLog entries in the other patches.

> +
>  2019-05-02  Didier Nadeau  <didier.nadeau@gmail.com>
>              Jan Vrany  <jan.vrany@fit.cvut.cz>
>  
> diff --git a/gdb/mi/mi-cmds.c b/gdb/mi/mi-cmds.c
> index 0aead954f9..e280e51daf 100644
> --- a/gdb/mi/mi-cmds.c
> +++ b/gdb/mi/mi-cmds.c
> @@ -41,7 +41,8 @@ insert_mi_cmd_entry (mi_cmd_up command)
>    const std::string &name = command->name ();
>  
>    if (mi_cmd_table.find (name) != mi_cmd_table.end ())
> -    return false;
> +    if (! mi_cmd_table[name]->can_be_redefined ())
> +      return false;

No space after !.

I'd combine the two ifs:

    if (mi_cmd_table.find (name) != mi_cmd_table.end ()
        && !mi_cmd_table[name]->can_be_redefined ())
      return false;

better even, avoid the second lookup:

    auto foo = mi_cmd_table.find (name);
    if (foo != mi_cmd_table.end () && !foo->can_be_redefined ())
      return false;

>  
>    mi_cmd_table[name] = std::move (command);
>  
> @@ -80,6 +81,13 @@ mi_command::mi_command (const char *name, int *suppress_notification)
>      m_suppress_notification (suppress_notification)
>  {}
>  
> +bool
> +mi_command::can_be_redefined()

Space before parens.

> +{
> +  return false;
> +}
> +
> +
>  
>  void
>  mi_command::invoke (struct mi_parse *parse)
> diff --git a/gdb/mi/mi-cmds.h b/gdb/mi/mi-cmds.h
> index 5ca7232fca..331b1349df 100644
> --- a/gdb/mi/mi-cmds.h
> +++ b/gdb/mi/mi-cmds.h
> @@ -143,6 +143,9 @@ public:
>    /* Execute the MI command.  */
>    void invoke (struct mi_parse *parse);
>  
> +  /* Return TRUE if command can be redefined, FALSE otherwise. */

"if THE command", or "if THIS command".

Double space after period.

> +  virtual bool can_be_redefined();

Space before parens.

> +
>  protected:
>    gdb::optional<scoped_restore_tmpl<int>> do_suppress_notification ();
>    virtual void do_invoke(struct mi_parse *parse) = 0;
> diff --git a/gdb/python/py-micmd.c b/gdb/python/py-micmd.c
> index e99632c97a..4f2b13f3c7 100644
> --- a/gdb/python/py-micmd.c
> +++ b/gdb/python/py-micmd.c
> @@ -171,6 +171,12 @@ mi_command_py::mi_command_py (const char *name, gdbpy_ref<> object)
>  {
>  }
>  
> +bool
> +mi_command_py::can_be_redefined()

Space before parens.


> +{
> +  return true;
> +}
> +
>  void
>  mi_command_py::do_invoke (struct mi_parse *parse)
>  {
> @@ -179,6 +185,7 @@ mi_command_py::do_invoke (struct mi_parse *parse)
>    if (parse->argv == NULL)
>      error (_("Problem parsing arguments: %s %s"), parse->command, parse->args);
>  
> +  std::string name (this->name ());

As principle, prefer copy-initialization when possible:

  std::string name = this->name ();

This style avoids unwanted explicit conversions, and
makes explicit conversions stand out, since they'll be
written with ()s.

I think this needs to be a copy because of what the 
"THIS must not be used" comment below says.

A comment to the effect would help.  Something like:

  /* Must save a copy of the name because the Python code may
     replace the very same command that is currently executing.
     See further below.  */
  std::string name = this->name ();

>    PyObject *obj = this->pyobj.get ();
>  
>    gdbpy_enter enter_py (get_current_arch (), current_language);
> @@ -187,15 +194,14 @@ mi_command_py::do_invoke (struct mi_parse *parse)
>  
>    if (!PyObject_HasAttr (obj, invoke_cst))
>        error (_("-%s: Python command object missing 'invoke' method."),
> -	     name ().c_str ());
> -
> +	     name.c_str ());
>  
>    gdbpy_ref<> argobj (PyList_New (parse->argc));
>    if (argobj == nullptr)
>      {
>        gdbpy_print_stack ();
>        error (_("-%s: failed to create the Python arguments list."),
> -	     name ().c_str ());
> +	     name.c_str ());
>      }
>  
>    for (int i = 0; i < parse->argc; ++i)
> @@ -205,11 +211,16 @@ mi_command_py::do_invoke (struct mi_parse *parse)
>        if (PyList_SetItem (argobj.get (), i, str.release ()) != 0)
>  	{
>  	  error (_("-%s: failed to create the Python arguments list."),
> -		 name ().c_str ());
> +		 name.c_str ());
>  	}
>      }
>  
>    gdb_assert (PyErr_Occurred () == NULL);
> +
> +  /* From this point on, THIS must not be used since Python code may replace
> +     the the very same command that is currently executing.  This in turn leads
> +     to desctruction of THIS making it invalid.  See insert_mi_cmd_entry. */

Double "the" in "the the".

Typo "desctruction".

Double space after period at the end.

> +
>    gdbpy_ref<> result (
>      PyObject_CallMethodObjArgs (obj, invoke_cst, argobj.get (), NULL));
>    if (PyErr_Occurred () != NULL)
> @@ -218,9 +229,9 @@ mi_command_py::do_invoke (struct mi_parse *parse)
>        gdb::unique_xmalloc_ptr<char> ex_msg (ex.to_string ());
>  
>        if (ex_msg == NULL || *ex_msg == '\0')
> -	error (_("-%s: failed to execute command"), name ().c_str ());
> +	error (_("-%s: failed to execute command"), name.c_str ());
>        else
> -	error (_("-%s: %s"), name ().c_str (), ex_msg.get ());
> +	error (_("-%s: %s"), name.c_str (), ex_msg.get ());
>      }
>    else
>      {
> @@ -233,7 +244,6 @@ void mi_command_py::finalize ()
>  {
>    this->pyobj.reset (nullptr);
>  }
> -
>  /* Initialize the MI command object.  */

Spurious deletion of that line.

>  
>  int
> diff --git a/gdb/python/py-micmd.h b/gdb/python/py-micmd.h
> index deadc0116b..0d61069ca5 100644
> --- a/gdb/python/py-micmd.h
> +++ b/gdb/python/py-micmd.h
> @@ -44,7 +44,7 @@ class mi_command_py : public mi_command
>  
>      mi_command_py (const char *name, gdbpy_ref<> object);
>  
> -
> +    bool can_be_redefined() override;
>      /* This is called just before shutting down a Python interpreter
>         to release python object implementing the command. */

Space before parens.

Add empty line between the new declaration and the unrelated
comment below.

Double space after period.

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 50+ messages in thread

* Re: [PATCH v3 5/5] mi/python: Add tests for python-defined MI commands
  2019-05-30 14:19     ` [PATCH v3 5/5] mi/python: Add tests for python-defined MI commands Jan Vrany
@ 2019-06-18 20:11       ` Pedro Alves
  0 siblings, 0 replies; 50+ messages in thread
From: Pedro Alves @ 2019-06-18 20:11 UTC (permalink / raw)
  To: Jan Vrany, gdb-patches

On 5/30/19 2:48 PM, Jan Vrany wrote:
> gdb/testsuite/Changelog:
> 
> 	* gdb.python/py-mi-cmd.exp: New file.
> 	* gdb.python/py-mi-cmd.py: New file.
> ---
>  gdb/testsuite/ChangeLog                |   5 +
>  gdb/testsuite/gdb.python/py-mi-cmd.exp | 132 +++++++++++++++++++++++++
>  gdb/testsuite/gdb.python/py-mi-cmd.py  |  54 ++++++++++
>  3 files changed, 191 insertions(+)
>  create mode 100644 gdb/testsuite/gdb.python/py-mi-cmd.exp
>  create mode 100644 gdb/testsuite/gdb.python/py-mi-cmd.py
> 
> diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
> index ba657cc615..26d14996f4 100644
> --- a/gdb/testsuite/ChangeLog
> +++ b/gdb/testsuite/ChangeLog
> @@ -1,3 +1,8 @@
> +2019-05-03  Jan Vrany  <jan.vrany@fit.cvut.cz>
> +
> +	* gdb.python/py-mi-cmd.exp: New file.
> +	* gdb.python/py-mi-cmd.py: New file.
> +
>  2019-05-24  Tom de Vries  <tdevries@suse.de>
>  
>  	* gdb.dwarf2/gdb-add-index.exp: New file.
> diff --git a/gdb/testsuite/gdb.python/py-mi-cmd.exp b/gdb/testsuite/gdb.python/py-mi-cmd.exp
> new file mode 100644
> index 0000000000..ba8670d5ea
> --- /dev/null
> +++ b/gdb/testsuite/gdb.python/py-mi-cmd.exp
> @@ -0,0 +1,132 @@
> +# Copyright (C) 2018 Free Software Foundation, Inc.

Don't forget to update the copyright years.

> +# 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/>.
> +
> +# Test custom MI commands implemented in Python.
> +
> +load_lib gdb-python.exp
> +load_lib mi-support.exp
> +set MIFLAGS "-i=mi2"
> +
> +gdb_exit
> +if {[mi_gdb_start]} {
> +    continue
> +}
> +
> +if {[lsearch -exact [mi_get_features] python] < 0} {
> +    unsupported "python support is disabled"
> +    return -1
> +}
> +
> +standard_testfile
> +#
> +# Start here
> +#
> +

Empty line after standard_testfile.

> +
> +mi_gdb_test "set python print-stack full" \
> +  ".*\\^done" \
> +  "set python print-stack full"
> +
> +mi_gdb_test "source ${srcdir}/${subdir}/${testfile}.py" \
> +  ".*\\^done" \
> +  "load python file"
> +
> +mi_gdb_test "python pycmd1('-pycmd')" \
> +  ".*\\^done" \
> +  "Define -pycmd MI command"

Lowercase "define".

> +
> +
> +mi_gdb_test "-pycmd int" \
> +  "\\^done,result=\"42\"" \
> +  "-pycmd int"
> +
> +mi_gdb_test "-pycmd str" \
> +  "\\^done,result=\"Hello world!\"" \
> +  "-pycmd str"
> +
> +mi_gdb_test "-pycmd ary" \
> +  "\\^done,result=\\\[\"Hello\",\"42\"\\\]" \
> +  "-pycmd ary"
> +
> +mi_gdb_test "-pycmd dct" \
> +  "\\^done,result={hello=\"world\",times=\"42\"}" \
> +  "-pycmd dct"
> +
> +mi_gdb_test "-pycmd bk1" \
> +  "\\^error,msg=\"Non-string object used as key: Bad Kay.\"" \

Is "Kay" a typo or on purpose?

Also, escape the "." (throughout).

> +  "-pycmd bk1"
> +
> +mi_gdb_test "-pycmd bk2" \
> +  "\\^error,msg=\"Non-string object used as key: 1.\"" \
> +  "-pycmd bk2"
> +
> +mi_gdb_test "-pycmd bk3" \
> +  "\\^error,msg=\"Non-string object used as key: __repr__ returned non-string .*" \
> +  "-pycmd bk3"
> +
> +mi_gdb_test "-pycmd tpl" \
> +  "\\^done,result=\\\[\"42\",\"Hello\"\\\]" \
> +  "-pycmd tpl"
> +
> +mi_gdb_test "-pycmd itr" \
> +  "\\^done,result=\\\[\"1\",\"2\",\"3\"\\\]" \
> +  "-pycmd itr"
> +
> +mi_gdb_test "-pycmd nn1" \
> +  "\\^done" \
> +  "-pycmd nn1"
> +
> +mi_gdb_test "-pycmd nn2" \
> +  "\\^done,result=\\\[\"None\"\\\]" \
> +  "-pycmd nn2"
> +
> +mi_gdb_test "-pycmd bogus" \
> +  "\\^error,msg=\"-pycmd: Invalid parameter: bogus\"" \
> +  "-pycmd bogus"
> +
> +mi_gdb_test "-pycmd exp" \
> +  "\\^error,msg=\"-pycmd: failed to execute command\"" \
> +  "-pycmd exp"
> +
> +mi_gdb_test "python pycmd2('-pycmd')" \
> +  ".*\\^done" \
> +  "Redefine -pycmd MI command from CLI command"

lowercase "Redefine".

> +
> +mi_gdb_test "-pycmd str" \
> +  "\\^done,result=\"Ciao!\"" \
> +  "-pycmd str"
> +
> +mi_gdb_test "-pycmd int" \
> +  "\\^error,msg=\"-pycmd: Invalid parameter: int\"" \
> +  "-pycmd int"
> +
> +mi_gdb_test "-pycmd red" \
> +    "\\^error,msg=\"-pycmd: Command redefined but we failing anyway\"" \

"but we're failing anyway" ?

> +  "Redefine -pycmd MI command from Python MI command"

Ditto re. lowercase.

> +
> +mi_gdb_test "-pycmd int" \
> +  "\\^done,result=\"42\"" \
> +  "-pycmd int"

You have duplicated test names.  See:

  https://sourceware.org/gdb/wiki/GDBTestcaseCookbook#Make_sure_test_messages_are_unique

> +
> +mi_gdb_test "python pycmd1('')" \
> +  ".*\\^error,msg=\"MI command name is empty.\"" \
> +  "empty MI command name"
> +
> +mi_gdb_test "python pycmd1('-')" \
> +  ".*\\^error,msg=\"MI command name does not start with '-' followed by at least one letter or digit.\"" \
> +  "invalid MI command name"
> +
> +mi_gdb_test "python pycmd1('-bad-character-@')" \
> +  ".*\\^error,msg=\"MI command name contains invalid character: @.\"" \
> +  "invalid character in MI command name"
> diff --git a/gdb/testsuite/gdb.python/py-mi-cmd.py b/gdb/testsuite/gdb.python/py-mi-cmd.py
> new file mode 100644
> index 0000000000..24aa475095
> --- /dev/null
> +++ b/gdb/testsuite/gdb.python/py-mi-cmd.py
> @@ -0,0 +1,54 @@
> +import gdb
> +

Missing copyright header.

> +class BadKey:
> +    def __repr__(self):
> +        return "Bad Kay"
> +
> +class ReallyBadKey:
> +    def __repr__(self):
> +        return BadKey()
> +
> +
> +class pycmd1(gdb.MICommand):
> +    def invoke(self, argv):
> +        if argv[0] == 'int':
> +            return 42
> +        elif argv[0] == 'str':
> +            return "Hello world!"
> +        elif argv[0] == 'ary':
> +            return [ 'Hello', 42 ]
> +        elif argv[0] == "dct":
> +            return { 'hello' : 'world', 'times' : 42}
> +        elif argv[0] == "bk1":
> +            return { BadKey() : 'world' }
> +        elif argv[0] == "bk2":
> +            return { 1 : 'world' }
> +        elif argv[0] == "bk3":
> +            return { ReallyBadKey() : 'world' }
> +        elif argv[0] == 'tpl':
> +            return ( 42 , 'Hello' )
> +        elif argv[0] == 'itr':
> +            return iter([1,2,3])
> +        elif argv[0] == 'nn1':
> +            return None
> +        elif argv[0] == 'nn2':
> +            return [ None ]
> +        elif argv[0] == 'red':
> +            pycmd2('-pycmd')
> +            return None
> +        elif argv[0] == 'exp':
> +            raise gdb.GdbError()
> +        else:
> +            raise gdb.GdbError("Invalid parameter: %s" % argv[0])
> +
> +
> +class pycmd2(gdb.MICommand):
> +    def invoke(self, argv):
> +        if argv[0] == 'str':
> +            return "Ciao!"
> +        elif argv[0] == 'red':
> +            pycmd1('-pycmd')
> +            raise gdb.GdbError("Command redefined but we failing anyway")
> +        else:
> +            raise gdb.GdbError("Invalid parameter: %s" % argv[0])
> +
> 
Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 50+ messages in thread

end of thread, other threads:[~2019-06-18 20:11 UTC | newest]

Thread overview: 50+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-01-03 22:30 [PATCH] MI: Add new command -complete Jan Vrany
2019-01-16  9:21 ` Jan Vrany
2019-01-17 20:29 ` Tom Tromey
2019-01-17 21:01   ` Jan Vrany
2019-01-28 12:41   ` [PATCH v2 0/2] " Jan Vrany
2019-01-28 12:41     ` [PATCH v2 2/2] " Jan Vrany
2019-01-28 12:41     ` [PATCH v2 1/2] MI: extract command completion logic from complete_command() Jan Vrany
2019-02-27 20:41       ` Pedro Alves
     [not found]     ` <9ddd13d90ac5d77067f5690743149be8a2dcdd1a.camel@fit.cvut.cz>
2019-02-13  9:24       ` [PATCH v2 0/2] MI: Add new command -complete Jan Vrany
2019-02-19  7:33         ` Jan Vrany
2019-02-20 21:20     ` Tom Tromey
2019-02-21 16:05       ` Jan Vrany
2019-02-26 19:49         ` Tom Tromey
2019-02-27 10:41           ` Jan Vrany
2019-02-27 20:41           ` Pedro Alves
2019-02-28 10:18             ` Jan Vrany
2019-03-05 20:53               ` Pedro Alves
2019-03-06 15:09                 ` Jan Vrany
2019-03-06 15:45                   ` Eli Zaretskii
2019-03-06 16:37                     ` Jan Vrany
2019-03-06 17:36                       ` Eli Zaretskii
2019-03-04 14:52     ` [PATCH v3 2/2] " Jan Vrany
2019-03-04 17:35       ` Eli Zaretskii
2019-04-03 19:23       ` Pedro Alves
2019-03-04 14:52     ` [PATCH v3 1/2] MI: extract command completion logic from complete_command() Jan Vrany
2019-03-04 14:52     ` [PATCH v3 0/2] MI: Add new command -complete Jan Vrany
2019-04-18 11:59     ` [PATCH v4 1/2] MI: extract command completion logic from complete_command() Jan Vrany
2019-04-18 11:59     ` [PATCH v4 2/2] MI: Add new command -complete Jan Vrany
2019-04-18 12:51       ` Eli Zaretskii
2019-04-18 14:15       ` Pedro Alves
2019-04-18 14:55         ` Jan Vrany
2019-04-18 16:14           ` Pedro Alves
2019-05-16 11:27           ` Jan Vrany
2019-05-16 17:31             ` Tom Tromey
2019-04-18 11:59     ` [PATCH v4 0/2] " Jan Vrany
2019-04-18 14:59       ` [PATCH v5 2/2] " Jan Vrany
2019-04-18 15:23         ` Eli Zaretskii
2019-04-18 14:59       ` [PATCH v5 0/2] " Jan Vrany
2019-04-18 14:59       ` [PATCH v5 1/2] MI: extract command completion logic from complete_command() Jan Vrany
2019-05-30 13:49     ` [PATCH v3 0/5] Create MI commands using python Jan Vrany
2019-06-10 12:20       ` Jan Vrany
2019-05-30 13:49     ` [PATCH v3 1/5] Use std::map for MI commands in mi-cmds.c Jan Vrany
2019-05-30 13:49     ` [PATCH v3 4/5] mi/python: Allow redefinition of python MI commands Jan Vrany
2019-06-18 20:03       ` Pedro Alves
2019-05-30 13:49     ` [PATCH v3 2/5] Use classes to represent MI Command instead of structures Jan Vrany
2019-06-18 19:38       ` Pedro Alves
2019-05-30 13:49     ` [PATCH v3 3/5] Create MI commands using python Jan Vrany
2019-06-18 19:43       ` Pedro Alves
2019-05-30 14:19     ` [PATCH v3 5/5] mi/python: Add tests for python-defined MI commands Jan Vrany
2019-06-18 20:11       ` Pedro Alves

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).