[2/4] Fix some aspects of python lazy string handling: python

Message ID 001a1140923ee67789054adc4683@google.com
State New, archived
Headers

Commit Message

Terekhov, Mikhail via Gdb-patches March 16, 2017, 5:20 p.m. UTC
  Doug Evans writes:
  > On Sun, Nov 6, 2016 at 5:14 PM, Doug Evans <dje@google.com> wrote:
  > > Hi. This is the python side of the patch set.
  > >
  > > 2016-11-06  Doug Evans  <dje@google.com>
  > >
  > >         PR python/17728, python/18439, python/18779
  > >         * python/py-lazy-string.c (lazy_string_object): Clarify use of
  > > LENGTH
  > >         member.  Change type of TYPE member to PyObject *.  All uses
  > > updated.
  > >         (stpy_convert_to_value): Fix handling of TYPE_CODE_PTR.
  > >         (gdbpy_create_lazy_string_object): Flag bad length values.
  > >         Handle TYPE_CODE_ARRAY with possibly different user-provided  
length.
  > >         Handle typedefs in incoming type.
  > >         (stpy_lazy_string_elt_type): New function.
  > >         (gdbpy_extract_lazy_string): Call it.
  > >         * python/py-value.c (valpy_lazy_string): Flag bad length  
values.
  > >         Fix handling of TYPE_CODE_PTR.  Handle TYPE_CODE_ARRAY.  Handle
  > >         typedefs in incoming type.
  >
  > Rebased, re-regression tested on amd64-linux, and committed.
  > [I'll post the committed patch as soon as I can get sendmail on my box
  > working again, bleah. We still don't allow attachments do we?]

Had to rework some local changes due to emacs version differences.

commit 34b433203b5f56149c27a8dfea21a921392cb158
Author: Doug Evans <dje@google.com>
Date:   Wed Mar 15 15:35:13 2017 -0700

     Fix various python lazy string bugs.

     gdb/ChangeLog:

             PR python/17728, python/18439, python/18779
             * python/py-lazy-string.c (lazy_string_object): Clarify use of  
LENGTH
             member.  Change type of TYPE member to PyObject *.  All uses  
updated.
             (stpy_convert_to_value): Fix handling of TYPE_CODE_PTR.
             (gdbpy_create_lazy_string_object): Flag bad length values.
             Handle TYPE_CODE_ARRAY with possibly different user-provided  
length.
             Handle typedefs in incoming type.
             (stpy_lazy_string_elt_type): New function.
             (gdbpy_extract_lazy_string): Call it.
             * python/py-value.c (valpy_lazy_string): Flag bad length values.
             Fix handling of TYPE_CODE_PTR.  Handle TYPE_CODE_ARRAY.  Handle
             typedefs in incoming type.

     gdb/testsuite/ChangeLog:

             PR python/17728, python/18439, python/18779
             * gdb.python/py-value.c (main) Delete locals sptr, sn.
             * gdb.python/py-lazy-string.c (pointer): New typedef.
             (main): New locals ptr, array, typedef_ptr.
             * gdb.python/py-value.exp: Move lazy string tests to ...
             * gdb.python/py-lazy-string.exp: ... here.  Add more tests for  
pointer,
             array, typedef lazy strings.

-  gdb_test "python print (snstr.address)" "0" "test lazy string address"
-}
-
-
  proc test_inferior_function_call {} {
      global gdb_prompt hex decimal

@@ -534,7 +511,6 @@ if ![runto_main] then {

  test_value_in_inferior
  test_inferior_function_call
-test_lazy_strings
  test_value_after_death

  # Test either C or C++ values.
  

Patch

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 14c9561ee3..7541627fd7 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,20 @@ 
  2017-03-16  Doug Evans  <dje@google.com>

+	PR python/17728, python/18439, python/18779
+	* python/py-lazy-string.c (lazy_string_object): Clarify use of LENGTH
+	member.  Change type of TYPE member to PyObject *.  All uses updated.
+	(stpy_convert_to_value): Fix handling of TYPE_CODE_PTR.
+	(gdbpy_create_lazy_string_object): Flag bad length values.
+	Handle TYPE_CODE_ARRAY with possibly different user-provided length.
+	Handle typedefs in incoming type.
+	(stpy_lazy_string_elt_type): New function.
+	(gdbpy_extract_lazy_string): Call it.
+	* python/py-value.c (valpy_lazy_string): Flag bad length values.
+	Fix handling of TYPE_CODE_PTR.  Handle TYPE_CODE_ARRAY.  Handle
+	typedefs in incoming type.
+
+2017-03-16  Doug Evans  <dje@google.com>
+
  	* guile/guile-internal.h (tyscm_scm_to_type): Declare.
  	* guile/scm-type.c (tyscm_scm_to_type): New function.

diff --git a/gdb/python/py-lazy-string.c b/gdb/python/py-lazy-string.c
index 4af4566ef6..ab3f411743 100644
--- a/gdb/python/py-lazy-string.c
+++ b/gdb/python/py-lazy-string.c
@@ -26,6 +26,7 @@ 

  typedef struct {
    PyObject_HEAD
+
    /*  Holds the address of the lazy string.  */
    CORE_ADDR address;

@@ -35,14 +36,21 @@  typedef struct {
        encoding when the sting is printed.  */
    char *encoding;

-  /* Holds the length of the string in characters.  If the
-     length is -1, then the string will be fetched and encoded up to
-     the first null of appropriate width.  */
+  /* If TYPE is an array: If the length is known, then this value is the
+     array's length, otherwise it is -1.
+     If TYPE is not an array: Then this value represents the string's  
length.
+     In either case, if the value is -1 then the string will be fetched and
+     encoded up to the first null of appropriate width.  */
    long length;

-  /*  This attribute holds the type that is represented by the lazy
-      string's type.  */
-  struct type *type;
+  /* This attribute holds the type of the string.
+     For example if the lazy string was created from a C "char*" then TYPE
+     represents a C "char*".
+     To get the type of the character in the string call
+     stpy_lazy_string_elt_type.
+     This is recorded as a PyObject so that we take advantage of support  
for
+     preserving the type should its owning objfile go away.  */
+  PyObject *type;
  } lazy_string_object;

  extern PyTypeObject lazy_string_object_type
@@ -88,11 +96,12 @@  stpy_get_type (PyObject *self, void *closure)
  {
    lazy_string_object *str_obj = (lazy_string_object *) self;

-  return type_to_type_object (str_obj->type);
+  Py_INCREF (str_obj->type);
+  return str_obj->type;
  }

  static PyObject *
-stpy_convert_to_value  (PyObject *self, PyObject *args)
+stpy_convert_to_value (PyObject *self, PyObject *args)
  {
    lazy_string_object *self_string = (lazy_string_object *) self;
    struct value *val = NULL;
@@ -106,7 +115,32 @@  stpy_convert_to_value  (PyObject *self, PyObject *args)

    TRY
      {
-      val = value_at_lazy (self_string->type, self_string->address);
+      struct type *type = type_object_to_type (self_string->type);
+      struct type *realtype;
+
+      gdb_assert (type != NULL);
+      realtype = check_typedef (type);
+      switch (TYPE_CODE (realtype))
+	{
+	case TYPE_CODE_PTR:
+	  /* If a length is specified we need to convert this to an array
+	     of the specified size.  */
+	  if (self_string->length != -1)
+	    {
+	      /* PR 20786: There's no way to specify an array of length zero.
+		 Record a length of [0,-1] which is how Ada does it.  Anything
+		 we do is broken, but this is one possible solution.  */
+	      type = lookup_array_range_type (TYPE_TARGET_TYPE (realtype),
+					      0, self_string->length - 1);
+	      val = value_at_lazy (type, self_string->address);
+	    }
+	  else
+	    val = value_from_pointer (type, self_string->address);
+	  break;
+	default:
+	  val = value_at_lazy (type, self_string->address);
+	  break;
+	}
      }
    CATCH (except, RETURN_MASK_ALL)
      {
@@ -125,11 +159,24 @@  stpy_dealloc (PyObject *self)
    xfree (self_string->encoding);
  }

+/* Low level routine to create a <gdb.LazyString> object.
+
+   Note: If TYPE is an array, LENGTH either must be -1 (meaning to use the
+   size of the array, which may itself be unknown in which case a length of
+   -1 is still used) or must be the length of the array.  */
+
  PyObject *
  gdbpy_create_lazy_string_object (CORE_ADDR address, long length,
-			   const char *encoding, struct type *type)
+				 const char *encoding, struct type *type)
  {
    lazy_string_object *str_obj = NULL;
+  struct type *realtype;
+
+  if (length < -1)
+    {
+      PyErr_SetString (PyExc_ValueError, _("Invalid length."));
+      return NULL;
+    }

    if (address == 0 && length != 0)
      {
@@ -146,6 +193,27 @@  gdbpy_create_lazy_string_object (CORE_ADDR address,  
long length,
        return NULL;
      }

+  realtype = check_typedef (type);
+  switch (TYPE_CODE (realtype))
+    {
+    case TYPE_CODE_ARRAY:
+      {
+	LONGEST array_length = -1;
+	LONGEST low_bound, high_bound;
+
+	if (get_array_bounds (realtype, &low_bound, &high_bound))
+	  array_length = high_bound - low_bound + 1;
+	if (length == -1)
+	  length = array_length;
+	else if (length != array_length)
+	  {
+	    PyErr_SetString (PyExc_ValueError, _("Invalid length."));
+	    return NULL;
+	  }
+	break;
+      }
+    }
+
    str_obj = PyObject_New (lazy_string_object, &lazy_string_object_type);
    if (!str_obj)
      return NULL;
@@ -156,7 +224,7 @@  gdbpy_create_lazy_string_object (CORE_ADDR address,  
long length,
      str_obj->encoding = NULL;
    else
      str_obj->encoding = xstrdup (encoding);
-  str_obj->type = type;
+  str_obj->type = type_to_type_object (type);

    return (PyObject *) str_obj;
  }
@@ -179,12 +247,35 @@  gdbpy_is_lazy_string (PyObject *result)
    return PyObject_TypeCheck (result, &lazy_string_object_type);
  }

+/* Return the type of a character in lazy string LAZY.  */
+
+static struct type *
+stpy_lazy_string_elt_type (lazy_string_object *lazy)
+{
+  struct type *type = type_object_to_type (lazy->type);
+  struct type *realtype;
+
+  gdb_assert (type != NULL);
+  realtype = check_typedef (type);
+
+  switch (TYPE_CODE (realtype))
+    {
+    case TYPE_CODE_PTR:
+    case TYPE_CODE_ARRAY:
+      return TYPE_TARGET_TYPE (realtype);
+    default:
+      /* This is done to preserve existing behaviour.  PR 20769.
+	 E.g., gdb.parse_and_eval("my_int_variable").lazy_string().type.  */
+      return realtype;
+    }
+}
+
  /* Extract the parameters from the lazy string object STRING.
     ENCODING may be set to NULL, if no encoding is found.  */

  void
  gdbpy_extract_lazy_string (PyObject *string, CORE_ADDR *addr,
-			   struct type **str_type,
+			   struct type **str_elt_type,
  			   long *length,
  			   gdb::unique_xmalloc_ptr<char> *encoding)
  {
@@ -195,7 +286,7 @@  gdbpy_extract_lazy_string (PyObject *string, CORE_ADDR  
*addr,
    lazy = (lazy_string_object *) string;

    *addr = lazy->address;
-  *str_type = lazy->type;
+  *str_elt_type = stpy_lazy_string_elt_type (lazy);
    *length = lazy->length;
    encoding->reset (lazy->encoding ? xstrdup (lazy->encoding) : NULL);
  }
diff --git a/gdb/python/py-value.c b/gdb/python/py-value.c
index eb3d307b19..124d398013 100644
--- a/gdb/python/py-value.c
+++ b/gdb/python/py-value.c
@@ -400,9 +400,19 @@  valpy_get_dynamic_type (PyObject *self, void *closure)
     A lazy string is a pointer to a string with an optional encoding and
     length.  If ENCODING is not given, encoding is set to None.  If an
     ENCODING is provided the encoding parameter is set to ENCODING, but
-   the string is not encoded.  If LENGTH is provided then the length
-   parameter is set to LENGTH, otherwise length will be set to -1 (first
-   null of appropriate with).  */
+   the string is not encoded.
+   If LENGTH is provided then the length parameter is set to LENGTH.
+   Otherwise if the value is an array of known length then the array's  
length
+   is used.  Otherwise the length will be set to -1 (meaning first null of
+   appropriate with).
+
+   Note: In order to not break any existing uses this allows creating
+   lazy strings from anything.  PR 20769.  E.g.,
+   gdb.parse_and_eval("my_int_variable").lazy_string().
+   "It's easier to relax restrictions than it is to impose them after the
+   fact."  So we should be flagging any unintended uses as errors, but it's
+   perhaps too late for that.  */
+
  static PyObject *
  valpy_lazy_string (PyObject *self, PyObject *args, PyObject *kw)
  {
@@ -416,16 +426,66 @@  valpy_lazy_string (PyObject *self, PyObject *args,  
PyObject *kw)
  				    &user_encoding, &length))
      return NULL;

+  if (length < -1)
+    {
+      PyErr_SetString (PyExc_ValueError, _("Invalid length."));
+      return NULL;
+    }
+
    TRY
      {
        scoped_value_mark free_values;
+      struct type *type, *realtype;
+      CORE_ADDR addr;

-      if (TYPE_CODE (value_type (value)) == TYPE_CODE_PTR)
-	value = value_ind (value);
+      type = value_type (value);
+      realtype = check_typedef (type);
+
+      switch (TYPE_CODE (realtype))
+	{
+	case TYPE_CODE_ARRAY:
+	  {
+	    LONGEST array_length = -1;
+	    LONGEST low_bound, high_bound;
+
+	    /* PR 20786: There's no way to specify an array of length zero.
+	       Record a length of [0,-1] which is how Ada does it.  Anything
+	       we do is broken, but this one possible solution.  */
+	    if (get_array_bounds (realtype, &low_bound, &high_bound))
+	      array_length = high_bound - low_bound + 1;
+	    if (length == -1)
+	      length = array_length;
+	    else if (array_length == -1)
+	      {
+		type = lookup_array_range_type (TYPE_TARGET_TYPE (realtype),
+						0, length - 1);
+	      }
+	    else if (length != array_length)
+	      {
+		/* We need to create a new array type with the
+		   specified length.  */
+		if (length > array_length)
+		  error (_("Length is larger than array size."));
+		type = lookup_array_range_type (TYPE_TARGET_TYPE (realtype),
+						low_bound,
+						low_bound + length - 1);
+	      }
+	    addr = value_address (value);
+	    break;
+	  }
+	case TYPE_CODE_PTR:
+	  /* If a length is specified we defer creating an array of the
+	     specified width until we need to.  */
+	  addr = value_as_address (value);
+	  break;
+	default:
+	  /* Should flag an error here.  PR 20769.  */
+	  addr = value_address (value);
+	  break;
+	}

-      str_obj = gdbpy_create_lazy_string_object (value_address (value),  
length,
-						 user_encoding,
-						 value_type (value));
+      str_obj = gdbpy_create_lazy_string_object (addr, length,  
user_encoding,
+						 type);
      }
    CATCH (except, RETURN_MASK_ALL)
      {
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index 713031cfe1..92138a4a4e 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,7 +1,19 @@ 
+<<<<<<< HEAD
  2017-03-16  Thomas Preud'homme  <thomas.preudhomme@arm.com>

  	* gdb.cp/m-static.exp: Fix expectation for prototype of
  	test5.single_constructor and single_constructor::single_constructor.
+=======
+2017-03-15  Doug Evans  <dje@google.com>
+
+	PR python/17728, python/18439, python/18779
+	* gdb.python/py-value.c (main) Delete locals sptr, sn.
+	* gdb.python/py-lazy-string.c (pointer): New typedef.
+	(main): New locals ptr, array, typedef_ptr.
+	* gdb.python/py-value.exp: Move lazy string tests to ...
+	* gdb.python/py-lazy-string.exp: ... here.  Add more tests for pointer,
+	array, typedef lazy strings.
+>>>>>>> Fix various python lazy string bugs.

  2017-03-14  Anton Kolesov  <anton.kolesov@synopsys.com>

diff --git a/gdb/testsuite/gdb.python/py-lazy-string.c  
b/gdb/testsuite/gdb.python/py-lazy-string.c
index 105c559663..41011ce99c 100644
--- a/gdb/testsuite/gdb.python/py-lazy-string.c
+++ b/gdb/testsuite/gdb.python/py-lazy-string.c
@@ -18,6 +18,9 @@ 
  int
  main ()
  {
+  const char *ptr = "pointer";
+  const char array[] = "array";
+  pointer typedef_ptr = "typedef pointer";
    const char *null = 0;

    return 0; /* break here */
diff --git a/gdb/testsuite/gdb.python/py-lazy-string.exp  
b/gdb/testsuite/gdb.python/py-lazy-string.exp
index e3054f629d..ff95a2e12b 100644
--- a/gdb/testsuite/gdb.python/py-lazy-string.exp
+++ b/gdb/testsuite/gdb.python/py-lazy-string.exp
@@ -34,9 +34,43 @@  if ![runto_main ] {
  gdb_breakpoint [gdb_get_line_number "break here"]
  gdb_continue_to_breakpoint "break here"

-gdb_test_no_output "python null = gdb.parse_and_eval(\"null\")"
+gdb_py_test_silent_cmd "python null = gdb.parse_and_eval(\"null\")" "get  
null value" 1

-gdb_test "python print(null.lazy_string(length=0).value())" \
-    "gdb.MemoryError: Cannot create a value from NULL.*Error while  
executing Python code."
+gdb_py_test_silent_cmd "python nullstr =  
null.lazy_string(length=0)" "create a null lazy string" 1
+gdb_test "python print (nullstr.length)" "0" "null lazy string length"
+gdb_test "python print (nullstr.address)" "0" "null lazy string address"
+gdb_test "python print (nullstr.type)" "const char \\*" "null lazy string  
type"
+gdb_test "python print(nullstr.value())" \
+    "gdb.MemoryError: Cannot create a value from NULL.*Error while  
executing Python code." \
+    "create value from NULL"
  gdb_test "python print(null.lazy_string(length=3).value())" \
-    "gdb.MemoryError: Cannot create a lazy string with address 0x0, and a  
non-zero length.*Error while executing Python code."
+    "gdb.MemoryError: Cannot create a lazy string with address 0x0, and a  
non-zero length.*Error while executing Python code." \
+    "null lazy string with non-zero length"
+gdb_test "python print(null.lazy_string(length=-2))" \
+    "ValueError: Invalid length.*Error while executing Python code." \
+    "bad length"
+
+foreach var_spec { { "ptr" "pointer" "const char \\*" -1 } \
+		   { "array" "array" "const char \\[6\\]" 6 } \
+		   { "typedef_ptr" "typedef pointer" "pointer" -1 } } {
+    set var [lindex $var_spec 0]
+    set value [lindex $var_spec 1]
+    set type [lindex $var_spec 2]
+    set length [lindex $var_spec 3]
+    with_test_prefix $var {
+	gdb_test "print $var" "\"$value\""
+	gdb_py_test_silent_cmd "python $var = gdb.history (0)" "get value from  
history" 1
+	gdb_py_test_silent_cmd "python l$var = $var.lazy_string()" "acquire lazy  
string" 1
+	gdb_test "python print ($var.type)" "$type" "string type name equality"
+	gdb_test "python print (l$var.type)" "$type" "lazy-string type name  
equality"
+	gdb_test "python print (l$var.length)" "$length" "lazy string length"
+	gdb_test "python print (l$var.value())" "\"$value\"" "lazy string value"
+	gdb_py_test_silent_cmd "python l2$var =  
$var.lazy_string(length=2)" "acquire lazy string, length 2" 1
+	gdb_test "python print (l2$var.length)" "2" "lazy string length 2"
+	gdb_test "python print (l2$var.value())" "\"[string range $value 0  
1]\"" "lazy string length 2 value"
+	# This test will have to wait until gdb can handle it. There's no way,
+	# currently, to internally specify an array of length zero in the C
+	# language support.  PR 20786
+	#gdb_test "python print  
($var.lazy_string(length=0).value())" "\"\"" "empty lazy string value"
+    }
+}
diff --git a/gdb/testsuite/gdb.python/py-value.c  
b/gdb/testsuite/gdb.python/py-value.c
index d90b15e7ee..b25b8a6c73 100644
--- a/gdb/testsuite/gdb.python/py-value.c
+++ b/gdb/testsuite/gdb.python/py-value.c
@@ -90,13 +90,11 @@  main (int argc, char *argv[])
    char nullst[17] = "divide\0et\0impera";
    void (*fp1) (void)  = &func1;
    int  (*fp2) (int, int) = &func2;
-  const char *sptr = "pointer";
    const char *embed = "embedded x\201\202\203\204";
    int a[3] = {1,2,3};
    int *p = a;
    int i = 2;
    int *ptr_i = &i;
-  const char *sn = 0;
    struct str *xstr;

    /* Prevent gcc from optimizing argv[] out.  */
diff --git a/gdb/testsuite/gdb.python/py-value.exp  
b/gdb/testsuite/gdb.python/py-value.exp
index d9a4d20bad..1781887c4a 100644
--- a/gdb/testsuite/gdb.python/py-value.exp
+++ b/gdb/testsuite/gdb.python/py-value.exp
@@ -317,29 +317,6 @@  proc test_value_in_inferior {} {
      "read string beyond declared size"
  }

-proc test_lazy_strings {} {
-
-  global hex
-
-  gdb_test "print sptr" "\"pointer\""
-  gdb_py_test_silent_cmd "python sptr = gdb.history (0)" "Get value from  
history" 1
-
-  gdb_py_test_silent_cmd "python lstr = sptr.lazy_string()" "Aquire lazy  
string" 1
-  gdb_test "python print (lstr.type)" "const char \*." "test lazy-string  
type name equality"
-  gdb_test "python print (sptr.type)" "const char \*." "test string type  
name equality"
-
-  # Prevent symbol on address 0x0 being printed.
-  gdb_test_no_output "set print symbol off"
-  gdb_test "print sn" "0x0"
-
-  gdb_py_test_silent_cmd "python snptr = gdb.history (0)" "Get value from  
history" 1
-  gdb_test "python snstr = snptr.lazy_string(length=5)" ".*Cannot create a  
lazy string with address.*" "test lazy string"
-  gdb_py_test_silent_cmd "python snstr =  
snptr.lazy_string(length=0)" "Succesfully create a lazy string" 1
-  gdb_test "python print (snstr.length)" "0" "test lazy string length"