public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [RFC] Extend gdb.parse_and_eval to accept a format string.
@ 2013-12-29 11:34 Siva Chandra
  0 siblings, 0 replies; only message in thread
From: Siva Chandra @ 2013-12-29 11:34 UTC (permalink / raw)
  To: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 1555 bytes --]

Hi,

Attached is a patch which extends gdb.parse_and_eval to accept a
format string. The idea was roughly put down by Doug here:
https://sourceware.org/ml/gdb-patches/2013-12/msg00709.html

My patch does not do exactly the same as what Doug did in his example;
Instead of using %V, I used ${N} syntax. If the string argument to
gdb.parse_and_eval is a format string, then a variable number of args
of type gdb.Value can follow. The number of gdb.Value args to be
passed is determined by the format string. The Nth gdb.Value arg is
referenced by "${N}" in the format string. The value of N starts at 0.

I have not made any docs or NEWS changes as I am not sure if the idea
in the patch is acceptable at all. I have however added tests.

        * parse.c (write_dollar_variable): Pass 0 for the "true_alias"
        arg of create_internalvar.
        * value.c (struct internalvar): New field "true_alias".
        (create_internalvar): New arg 'true_alias'.
        (create_internalvar_type_lazy): Pass 0 for the "true_alias" arg
        of create_internalvar.
        (value_of_internalvar): Do not set VALUE_LVAL to lval_internalvar
        if "true_alias" field of internalvar is set.
        * value.h (create_internalvar): Change prototype.
        * python/python.c (get_python_internalvar_name): New function.
        (validate_expression): New function.
        (parse_args_for_parse_and_eval): New function.
        (gdbpy_parse_and_eval): Use parse_args_for_parse_and_eval.

        testsuite/
        * gdb.python/py-value.exp: Add new tests.

[-- Attachment #2: parse_and_eval_patch_v1.txt --]
[-- Type: text/plain, Size: 9410 bytes --]

diff --git a/gdb/parse.c b/gdb/parse.c
index 4b9ca5d..14328a0 100644
--- a/gdb/parse.c
+++ b/gdb/parse.c
@@ -715,7 +715,7 @@ write_dollar_variable (struct stoken str)
   /* Any other names are assumed to be debugger internal variables.  */
 
   write_exp_elt_opcode (OP_INTERNALVAR);
-  write_exp_elt_intern (create_internalvar (copy_name (str) + 1));
+  write_exp_elt_intern (create_internalvar (copy_name (str) + 1, 0));
   write_exp_elt_opcode (OP_INTERNALVAR);
   return;
 handle_last:
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 55bb6cf..9eb2f41 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -33,6 +33,7 @@
 #include "readline/tilde.h"
 #include "python.h"
 #include "cli/cli-utils.h"
+#include "common/buffer.h"
 
 #include <ctype.h>
 
@@ -41,6 +42,11 @@ static const char python_excp_none[] = "none";
 static const char python_excp_full[] = "full";
 static const char python_excp_message[] = "message";
 
+/* Prefix string to be used in names of internalvar objects when created for
+   expression evaluation via gdb.parse_and_eval(...).  */
+
+static const char python_internalvar_prefix[] = "__python__internalvar__";
+
 /* "set python print-stack" choices.  */
 static const char *const python_excp_enums[] =
   {
@@ -717,21 +723,156 @@ gdbpy_decode_line (PyObject *self, PyObject *args)
   return return_result;
 }
 
+static char *
+get_python_internalvar_name (int i)
+{
+  struct buffer name_buf;
+
+  buffer_init (&name_buf);
+  buffer_xml_printf (&name_buf, "%s%d", python_internalvar_prefix, i);
+  buffer_grow_str0 (&name_buf, "");
+
+  return buffer_finish (&name_buf);
+}
+
+static char *
+validate_expression (const char *pexpr, int argc)
+{
+  struct buffer expr_buf;
+  int i;
+  int pexpr_len = strlen (pexpr);
+
+  buffer_init (&expr_buf);
+  for (i = 0; i < pexpr_len - 3; i++)
+    {
+      if (pexpr[i] == '$' && pexpr[i + 1] == '{')
+	{
+	  int j = i + 2;
+	  struct buffer int_buf;
+	  int val_n;
+	  char *internalvar_name, *int_str;
+
+	  buffer_init (&int_buf);
+	  while (pexpr[j] != '}')
+	    {
+	      buffer_grow (&int_buf, &pexpr[j], 1);
+	      j++;
+	      if (j >= pexpr_len)
+		{
+		  buffer_free (&expr_buf);
+		  buffer_free (&int_buf);
+
+		  return NULL;
+		}
+	    }
+
+	  buffer_grow_str0 (&int_buf, "");
+	  int_str = buffer_finish (&int_buf);
+	  val_n = atoi (int_str);
+	  xfree (int_str);
+	  if (val_n >= (argc - 1) || val_n < 0)
+	    {
+	      buffer_free (&expr_buf);
+
+	      return NULL;
+	    }
+
+	  i = j;
+	  internalvar_name = get_python_internalvar_name (val_n);
+	  buffer_grow_str (&expr_buf, "$");
+	  buffer_grow_str (&expr_buf, internalvar_name);
+	  xfree (internalvar_name);
+	}
+      else
+	buffer_grow (&expr_buf, &pexpr[i], 1);
+    }
+  buffer_grow_str0 (&expr_buf, &pexpr[i]);
+
+  return buffer_finish (&expr_buf);
+}
+
+static char *
+parse_args_for_parse_and_eval (PyObject *args)
+{
+  LONGEST argc = PyTuple_GET_SIZE (args), i;
+  PyObject *format_string;
+  char *pexpr, *expr, *tmp;
+
+  if (argc == 0)
+    {
+      PyErr_SetString (PyExc_SyntaxError,
+		       _("Insufficient number of arguments."));
+
+      return NULL;
+    }
+
+  format_string = PyTuple_GET_ITEM (args, 0);
+  if (!gdbpy_is_string (format_string))
+    {
+      PyErr_SetString (PyExc_TypeError, _("First argument is not a string."));
+
+      return NULL;
+    }
+  pexpr = python_string_to_host_string (format_string);
+  if (pexpr == NULL)
+    return NULL;
+
+  expr = validate_expression (pexpr, argc);
+  if (expr == NULL)
+    {
+      PyErr_SetString (PyExc_ValueError, _("Incorrect expression format."));
+
+      return NULL;
+    }
+
+  for (i = 1; i < argc; i++)
+    {
+      PyObject *value_obj = PyTuple_GET_ITEM (args, i);
+      struct value *v;
+      struct internalvar *var;
+      char *var_name;
+
+      if (!PyObject_TypeCheck (value_obj, &value_object_type))
+	{
+	  PyErr_SetString (PyExc_TypeError,
+			   _("Arguments 1 and beyond are not gdb.Value "
+                             "objects."));
+	  xfree (expr);
+
+	  return NULL;
+	}
+
+      var_name = get_python_internalvar_name (i - 1);
+      var = lookup_only_internalvar (var_name);
+      if (var == NULL)
+	var = create_internalvar (var_name, 1);
+      v = value_object_to_value (value_obj);
+      set_internalvar (var, v);
+
+      xfree (var_name);
+    }
+
+  return expr;
+}
+
 /* Parse a string and evaluate it as an expression.  */
 static PyObject *
 gdbpy_parse_and_eval (PyObject *self, PyObject *args)
 {
-  const char *expr_str;
+  char *expr_str;
   struct value *result = NULL;
   volatile struct gdb_exception except;
 
-  if (!PyArg_ParseTuple (args, "s", &expr_str))
+  expr_str = parse_args_for_parse_and_eval (args);
+  if (expr_str == NULL)
     return NULL;
 
   TRY_CATCH (except, RETURN_MASK_ALL)
     {
       result = parse_and_eval (expr_str);
     }
+
+  xfree (expr_str);
   GDB_PY_HANDLE_EXCEPTION (except);
 
   return value_to_value_object (result);
diff --git a/gdb/testsuite/gdb.python/py-value.exp b/gdb/testsuite/gdb.python/py-value.exp
index a052104..3ebe23b 100644
--- a/gdb/testsuite/gdb.python/py-value.exp
+++ b/gdb/testsuite/gdb.python/py-value.exp
@@ -467,6 +467,24 @@ proc test_parse_and_eval {} {
     "parse_and_eval type test"
 }
 
+# Few more tests of gdb.parse_and_eval.  These tests require execution.
+proc test_parse_and_eval1 {} {
+  gdb_test_no_output "python a = gdb.parse_and_eval('a')" \
+    "test_parse_and_eval1: init a"
+  gdb_test "python print gdb.parse_and_eval('$\{0\}\[1\]', a)" "2" \
+    "test_parse_and_eval1: use subscript on $\{0\}"
+
+  gdb_test_no_output "python s = gdb.parse_and_eval('s')" \
+    "test_parse_and_eval1: init s"
+  gdb_test "python print gdb.parse_and_eval('$\{0\}.b', s)" "5" \
+    "test_parse_and_eval1: use '.' on $\{0\}"
+
+  gdb_test_no_output "python s_ptr = gdb.parse_and_eval('&s')" \
+    "test_parse_and_eval1: init s_ptr"
+  gdb_test "python print gdb.parse_and_eval('$\{0\}->a', s_ptr)" "3" \
+    "test_parse_and_eval1: use '->' on $\{0\}"
+}
+
 # Test that values are hashable.
 proc test_value_hash {} {
   gdb_py_test_multiple "Simple Python value dictionary" \
@@ -509,6 +527,7 @@ if ![runto_main] then {
 }
 
 test_value_in_inferior
+test_parse_and_eval1
 test_inferior_function_call
 test_lazy_strings
 test_value_after_death
diff --git a/gdb/value.c b/gdb/value.c
index 25c9f32..22589d6 100644
--- a/gdb/value.c
+++ b/gdb/value.c
@@ -1780,6 +1780,15 @@ struct internalvar
   struct internalvar *next;
   char *name;
 
+  /* If the internal variable is of INTERNALVAR_VALUE kind (see below),
+     and is going to be used as a true alias for the underlying GDB
+     value, then this field is 1.  It is 0 for all other cases.  The
+     word 'true' is to imply that this internal variable will be used
+     exactly like the underlying GDB value (and not just a convenience
+     name for a GDB value).  */
+
+  int true_alias;
+
   /* We support various different kinds of content of an internal variable.
      enum internalvar_kind specifies the kind, and union internalvar_data
      provides the data associated with this particular kind.  */
@@ -1925,15 +1934,19 @@ complete_internalvar (const char *name)
 }
 
 /* Create an internal variable with name NAME and with a void value.
-   NAME should not normally include a dollar sign.  */
+   NAME should not normally include a dollar sign.  If the internal
+   variable is going to be set to INTERNALVAR_VALUE kind and is going
+   to be used as true alias for the GDB value it represents, then set
+   TRUE_ALIAS to 1.  Set it to 0 otherwise.  */
 
 struct internalvar *
-create_internalvar (const char *name)
+create_internalvar (const char *name, int true_alias)
 {
   struct internalvar *var;
 
   var = (struct internalvar *) xmalloc (sizeof (struct internalvar));
   var->name = concat (name, (char *)NULL);
+  var->true_alias = true_alias ? 1 : 0;
   var->kind = INTERNALVAR_VOID;
   var->next = internalvars;
   internalvars = var;
@@ -1952,7 +1965,7 @@ create_internalvar_type_lazy (const char *name,
 			      const struct internalvar_funcs *funcs,
 			      void *data)
 {
-  struct internalvar *var = create_internalvar (name);
+  struct internalvar *var = create_internalvar (name, 0);
 
   var->kind = INTERNALVAR_MAKE_VALUE;
   var->u.make_value.functions = funcs;
@@ -1991,7 +2004,7 @@ lookup_internalvar (const char *name)
   if (var)
     return var;
 
-  return create_internalvar (name);
+  return create_internalvar (name, 0);
 }
 
 /* Return current value of internal variable VAR.  For variables that
@@ -2074,7 +2087,7 @@ value_of_internalvar (struct gdbarch *gdbarch, struct internalvar *var)
      want.  */
 
   if (var->kind != INTERNALVAR_MAKE_VALUE
-      && val->lval != lval_computed)
+      && val->lval != lval_computed && !var->true_alias)
     {
       VALUE_LVAL (val) = lval_internalvar;
       VALUE_INTERNALVAR (val) = var;
diff --git a/gdb/value.h b/gdb/value.h
index 6b158df..67e403a 100644
--- a/gdb/value.h
+++ b/gdb/value.h
@@ -799,7 +799,8 @@ extern void set_internalvar_component (struct internalvar *var,
 
 extern struct internalvar *lookup_only_internalvar (const char *name);
 
-extern struct internalvar *create_internalvar (const char *name);
+extern struct internalvar *create_internalvar (const char *name,
+					       int true_alias);
 
 extern VEC (char_ptr) *complete_internalvar (const char *name);
 

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2013-12-29 11:34 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-12-29 11:34 [RFC] Extend gdb.parse_and_eval to accept a format string Siva Chandra

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).