Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
* Python: add field access by name and standard python mapping methods to gdb.Type
@ 2011-09-16  4:09 Paul Koning
  2011-09-16  7:36 ` Eli Zaretskii
  2011-09-16 10:47 ` Phil Muldoon
  0 siblings, 2 replies; 26+ messages in thread
From: Paul Koning @ 2011-09-16  4:09 UTC (permalink / raw)
  To: gdb-patches

The attached proposed patch adds access to struct and enum fields by their name, just as field names can be used on gdb.Value objects.  It also adds the standard Python methods for mappings (dictionary-like objects), so that the kind of things that Python programmers would expect to work on objects that can be indexed by name indeed do work.  For example, you can iterate through the fields, or query if a field name exists.  

For a change like this that adds a new feature which uses a whole pile of new functions, is the sort of ChangeLog entry shown below the right way to document the change, or should I write this in a different way?

	paul

ChangeLog:

2011-09-15  Paul Koning  <paul_koning@dell.com>

	* python/py-type.c: Add standard Python mapping methods to
	gdb.Type object
	(make_fielditem, typy_field_names, typy_items, typy_length,
	typy_get, typy_has_key, typy_make_iter, typy_iterkeys,
	typy_iteritems, typy_itervalues, typy_iter, typy_iterator_iter,
	typy_iterator_iternext, typy_iterator_dealloc): New.
	(gdb.TypeIterator): New Python type.
	* python/python-internal.h (gdbpy_iter_kind): New.
	* doc/gdb.texinfo (gdb.Type): Document field access by dictionary
	key syntax.

testsuite/ChangeLog:

2011-09-15  Paul Koning  <pkoning@equallogic.com>

	* gdb.python/py-type.c (enum E): New.
	* gdb.python/py-type.exp (test_fields): Add tests for Python
	mapping access to fields.
	(test_enums): New test for field access on enums.

Index: doc/gdb.texinfo
===================================================================
RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v
retrieving revision 1.861
diff -u -r1.861 gdb.texinfo
--- doc/gdb.texinfo	15 Sep 2011 16:09:42 -0000	1.861
+++ doc/gdb.texinfo	15 Sep 2011 18:34:41 -0000
@@ -21537,6 +21537,19 @@
 If the named type cannot be found, it will throw an exception.
 @end defun
 
+If the type is a structure or class type, or an enum type, the fields
+of that type can be accessed using the Python @dfn{dictionary syntax}.
+For example, if @code{some_type} is a @code{gdb.Type} instance holding
+a structure type, you can access its @code{foo} element with:
+
+@smallexample
+bar = some_type['foo']
+@end smallexample
+
+@code{bar} will be a @code{gdb.Field} object; see below under the
+description of the @code{fields()} method for a description of the
+gdb.Field class.
+
 An instance of @code{Type} has the following attributes:
 
 @table @code
@@ -21570,7 +21583,7 @@
 represented as fields.  If the type has no fields, or does not fit
 into one of these categories, an empty sequence will be returned.
 
-Each field is an object, with some pre-defined attributes:
+Each field is an @code{gdb.Field} object, with some pre-defined attributes:
 @table @code
 @item bitpos
 This attribute is not available for @code{static} fields (as in
Index: python/py-type.c
===================================================================
RCS file: /cvs/src/src/gdb/python/py-type.c,v
retrieving revision 1.21
diff -u -r1.21 py-type.c
--- python/py-type.c	15 Sep 2011 18:33:15 -0000	1.21
+++ python/py-type.c	15 Sep 2011 18:34:41 -0000
@@ -55,6 +55,19 @@
 
 static PyTypeObject field_object_type;
 
+/* A type iterator object.  */
+typedef struct {
+  PyObject_HEAD
+  /* The current field index.  */
+  int field;
+  /* What to return.  */
+  enum gdbpy_iter_kind kind;
+  /* Pointer back to the original source type object.  */
+  struct pyty_type_object *source;
+} typy_iterator_object;
+
+static PyTypeObject type_iterator_object_type;
+
 /* This is used to initialize various gdb.TYPE_ constants.  */
 struct pyty_code
 {
@@ -210,12 +223,64 @@
   return NULL;
 }
 
-/* Return a sequence of all fields.  Each field is a dictionary with
-   some pre-defined keys.  */
+/* Helper function to return the name of a field, as a Python object.  */
 static PyObject *
-typy_fields (PyObject *self, PyObject *args)
+field_name (struct type *type, int field)
 {
   PyObject *result;
+
+  if (TYPE_FIELD_NAME (type, field))
+    result = PyString_FromString (TYPE_FIELD_NAME (type, field));
+  else
+    {
+      result = Py_None;
+      Py_INCREF (result);
+    }
+  return result;
+}
+
+static PyObject *
+make_fielditem (struct type *type, int i, enum gdbpy_iter_kind kind)
+{
+  PyObject *item = NULL, *key = NULL, *value = NULL;
+
+  switch (kind)
+    {
+    case iter_items:
+      key = field_name (type, i);
+      if (key == NULL)
+	goto fail;
+      value = convert_field (type, i);
+      if (value == NULL)
+	goto fail;
+      item = PyTuple_New (2);
+      if (item == NULL)
+	goto fail;
+      PyTuple_SET_ITEM (item, 0, key);
+      PyTuple_SET_ITEM (item, 1, value);
+      break;
+    case iter_keys:
+      item = field_name (type, i);
+      break;
+    case iter_values:
+      item =  convert_field (type, i);
+      break;
+    }
+  return item;
+  
+ fail:
+  Py_XDECREF (key);
+  Py_XDECREF (value);
+  Py_XDECREF (item);
+  return NULL;
+}
+
+/* Return a sequence of all field names, fields, or (name, field) pairs.
+   Each field is a dictionary with some pre-defined keys.  */
+static PyObject *
+typy_fields_items (PyObject *self, enum gdbpy_iter_kind kind)
+{
+  PyObject *result = NULL, *item = NULL;
   int i;
   struct type *type = ((type_object *) self)->type;
   volatile struct gdb_exception except;
@@ -230,26 +295,49 @@
      then memoize the result (and perhaps make Field.type() lazy).
      However, that can lead to cycles.  */
   result = PyList_New (0);
-
+  if (result == NULL)
+    return NULL;
+  
   for (i = 0; i < TYPE_NFIELDS (type); ++i)
     {
-      PyObject *dict = convert_field (type, i);
-
-      if (!dict)
-	{
-	  Py_DECREF (result);
-	  return NULL;
-	}
-      if (PyList_Append (result, dict))
-	{
-	  Py_DECREF (dict);
-	  Py_DECREF (result);
-	  return NULL;
-	}
-      Py_DECREF (dict);
+      item = make_fielditem (type, i, kind);
+      if (!item)
+	goto fail;
+      if (PyList_Append (result, item))
+	goto fail;
+      Py_DECREF (item);
     }
 
   return result;
+
+ fail:
+  Py_XDECREF (item);
+  Py_XDECREF (result);
+  return NULL;
+}
+
+/* Return a sequence of all fields.  Each field is a dictionary with
+   some pre-defined keys.  */
+static PyObject *
+typy_fields (PyObject *self, PyObject *args)
+{
+  return typy_fields_items (self, iter_values);
+}
+
+/* Return a sequence of all field names.  Each field is a dictionary with
+   some pre-defined keys.  */
+static PyObject *
+typy_field_names (PyObject *self, PyObject *args)
+{
+  return typy_fields_items (self, iter_keys);
+}
+
+/* Return a sequence of all (name, fields) pairs.  Each field is a 
+   dictionary with some pre-defined keys.  */
+static PyObject *
+typy_items (PyObject *self, PyObject *args)
+{
+  return typy_fields_items (self, iter_items);
 }
 
 /* Return the type's tag, or None.  */
@@ -1000,6 +1088,194 @@
   type->ob_type->tp_free (type);
 }
 
+/* Return number of fields ("length" of the field dictionary).  */
+static Py_ssize_t
+typy_length (PyObject *self)
+{
+  struct type *type = ((type_object *) self)->type;
+
+  return TYPE_NFIELDS (type);
+}
+
+/* Return a field object for the field named by the argument.  */
+static PyObject *
+typy_getitem (PyObject *self, PyObject *key)
+{
+  struct type *type = ((type_object *) self)->type;
+  char *field;
+  int i;
+  
+  field = python_string_to_host_string (key);
+  if (field == NULL)
+    return NULL;
+
+  /* We want just fields of this type, not of base types, so instead of 
+     using lookup_struct_elt_type, portions of that function are
+     copied here.  */
+
+  for (;;)
+    {
+      CHECK_TYPEDEF (type);
+      if (TYPE_CODE (type) != TYPE_CODE_PTR
+	  && TYPE_CODE (type) != TYPE_CODE_REF)
+	break;
+      type = TYPE_TARGET_TYPE (type);
+    }
+
+  for (i = 0; i < TYPE_NFIELDS (type); i++)
+    {
+      char *t_field_name = TYPE_FIELD_NAME (type, i);
+
+      if (t_field_name && (strcmp_iw (t_field_name, field) == 0))
+	{
+	  return convert_field (type, i);
+	}
+    }
+  PyErr_SetObject (PyExc_KeyError, key);
+  return NULL;
+}
+
+/* Implement the "get" method on the type object.  This is the 
+   same as getitem if the key is present, but returns the supplied
+   default value or None if the key is not found.  */
+static PyObject *
+typy_get (PyObject *self, PyObject *args)
+{
+  PyObject *key, *defval = Py_None, *result;
+  
+  if (!PyArg_UnpackTuple (args, "get", 1, 2, &key, &defval))
+    return NULL;
+  
+  result = typy_getitem (self, key);
+  if (result != NULL)
+    return result;
+  
+  /* getitem returned error, clear the exception status and
+     return the defval instead.  */
+  PyErr_Clear ();
+  Py_INCREF (defval);
+  return defval;
+}
+
+/* Implement the "has_key" method on the type object.  */
+static PyObject *
+typy_has_key (PyObject *self, PyObject *args)
+{
+  struct type *type = ((type_object *) self)->type;
+  char *field;
+  int i;
+  
+  if (!PyArg_ParseTuple (args, "s", &field))
+    return NULL;
+
+  /* We want just fields of this type, not of base types, so instead of 
+     using lookup_struct_elt_type, portions of that function are
+     copied here.  */
+
+  for (;;)
+    {
+      CHECK_TYPEDEF (type);
+      if (TYPE_CODE (type) != TYPE_CODE_PTR
+	  && TYPE_CODE (type) != TYPE_CODE_REF)
+	break;
+      type = TYPE_TARGET_TYPE (type);
+    }
+
+  for (i = 0; i < TYPE_NFIELDS (type); i++)
+    {
+      char *t_field_name = TYPE_FIELD_NAME (type, i);
+
+      if (t_field_name && (strcmp_iw (t_field_name, field) == 0))
+	Py_RETURN_TRUE;
+    }
+  Py_RETURN_FALSE;
+}
+
+/* Make an iterator object to iterate over keys, values, or items.  */
+static PyObject *
+typy_make_iter (PyObject *self, enum gdbpy_iter_kind kind)
+{
+  typy_iterator_object *typy_iter_obj;
+
+  typy_iter_obj = PyObject_New (typy_iterator_object,
+				&type_iterator_object_type);
+  if (typy_iter_obj == NULL)
+      return NULL;
+
+  typy_iter_obj->field = 0;
+  typy_iter_obj->kind = kind;
+  Py_INCREF (self);
+  typy_iter_obj->source = (type_object *) self;
+
+  return (PyObject *) typy_iter_obj;
+}
+
+/* iteritems() method.  */
+static PyObject *
+typy_iteritems (PyObject *self, PyObject *args)
+{
+  return typy_make_iter (self, iter_items);
+}
+
+/* iterkeys() method.  */
+static PyObject *
+typy_iterkeys (PyObject *self, PyObject *args)
+{
+  return typy_make_iter (self, iter_keys);
+}
+
+/* Iterating over the class, same as iterkeys
+   except for the function signature.  */
+static PyObject *
+typy_iter (PyObject *self)
+{
+  return typy_make_iter (self, iter_keys);
+}
+
+/* itervalues() method.  */
+static PyObject *
+typy_itervalues (PyObject *self, PyObject *args)
+{
+  return typy_make_iter (self, iter_values);
+}
+
+/* Return a reference to the type iterator.  */
+static PyObject *
+typy_iterator_iter (PyObject *self)
+{
+  Py_INCREF (self);
+  return self;
+}
+
+/* Return the next field in the iteration through the list of fields
+   of the type.  */
+static PyObject *
+typy_iterator_iternext (PyObject *self)
+{
+  typy_iterator_object *iter_obj = (typy_iterator_object *) self;
+  struct type *type = iter_obj->source->type;
+  int i;
+  PyObject *result;
+  
+  if (iter_obj->field < TYPE_NFIELDS (type))
+    {
+      result = make_fielditem (type, iter_obj->field, iter_obj->kind);
+      if (result != NULL)
+	iter_obj->field++;
+      return result;
+    }
+
+  return NULL;
+}
+
+static void
+typy_iterator_dealloc (PyObject *obj)
+{
+  typy_iterator_object *iter_obj = (typy_iterator_object *) obj;
+
+  Py_DECREF (iter_obj->source);
+}
+
 /* Create a new Type referring to TYPE.  */
 PyObject *
 type_to_type_object (struct type *type)
@@ -1067,6 +1343,8 @@
     return;
   if (PyType_Ready (&field_object_type) < 0)
     return;
+  if (PyType_Ready (&type_iterator_object_type) < 0)
+    return;
 
   for (i = 0; pyty_codes[i].name; ++i)
     {
@@ -1080,6 +1358,10 @@
   Py_INCREF (&type_object_type);
   PyModule_AddObject (gdb_module, "Type", (PyObject *) &type_object_type);
 
+  Py_INCREF (&type_iterator_object_type);
+  PyModule_AddObject (gdb_module, "TypeIterator",
+		      (PyObject *) &type_iterator_object_type);
+
   Py_INCREF (&field_object_type);
   PyModule_AddObject (gdb_module, "Field", (PyObject *) &field_object_type);
 }
@@ -1102,13 +1384,35 @@
   { "array", typy_array, METH_VARARGS,
     "array (N) -> Type\n\
 Return a type which represents an array of N objects of this type." },
+   { "__contains__", typy_has_key, METH_VARARGS,
+     "T.__contains__(k) -> True if T has a field named k, else False" },
   { "const", typy_const, METH_NOARGS,
     "const () -> Type\n\
 Return a const variant of this type." },
   { "fields", typy_fields, METH_NOARGS,
-    "field () -> list\n\
-Return a sequence holding all the fields of this type.\n\
-Each field is a dictionary." },
+    "fields () -> list\n\
+Return a list holding all the fields of this type.\n\
+Each field is a gdb.Field object." },
+  { "get", typy_get, METH_VARARGS,
+    "T.get(k[,default]) -> returns field named k in T, if it exists;\n\
+otherwise returns default, if supplied, or None if not." },
+  { "has_key", typy_has_key, METH_VARARGS,
+    "T.has_key(k) -> True if T has a field named k, else False" },
+  { "items", typy_items, METH_NOARGS,
+    "items () -> list\n\
+Return a list of (name, field) pairs of this type.\n\
+Each field is a gdb.Field object." },
+  { "iteritems", typy_iteritems, METH_NOARGS,
+    "iteritems () -> an iterator over the (name, field)\n\
+pairs of this type.  Each field is a gdb.Field object." },
+  { "iterkeys", typy_iterkeys, METH_NOARGS,
+    "iterkeys () -> an iterator over the field names of this type." },
+  { "itervalues", typy_itervalues, METH_NOARGS,
+    "itervalues () -> an iterator over the fields of this type.\n\
+Each field is a gdb.Field object." },
+  { "keys", typy_field_names, METH_NOARGS,
+    "keys () -> list\n\
+Return a list holding all the fields names of this type." },
   { "pointer", typy_pointer, METH_NOARGS,
     "pointer () -> Type\n\
 Return a type of pointer to this type." },
@@ -1130,12 +1434,22 @@
   { "unqualified", typy_unqualified, METH_NOARGS,
     "unqualified () -> Type\n\
 Return a variant of this type without const or volatile attributes." },
+  { "values", typy_fields, METH_NOARGS,
+    "values () -> list\n\
+Return a list holding all the fields of this type.\n\
+Each field is a gdb.Field object." },
   { "volatile", typy_volatile, METH_NOARGS,
     "volatile () -> Type\n\
 Return a volatile variant of this type" },
   { NULL }
 };
 
+static PyMappingMethods typy_mapping = {
+  typy_length,
+  typy_getitem,
+  NULL				  /* no "set" method */
+};
+
 static PyTypeObject type_object_type =
 {
   PyObject_HEAD_INIT (NULL)
@@ -1151,7 +1465,7 @@
   0,				  /*tp_repr*/
   0,				  /*tp_as_number*/
   0,				  /*tp_as_sequence*/
-  0,				  /*tp_as_mapping*/
+  &typy_mapping,		  /*tp_as_mapping*/
   0,				  /*tp_hash */
   0,				  /*tp_call*/
   typy_str,			  /*tp_str*/
@@ -1164,7 +1478,7 @@
   0,				  /* tp_clear */
   typy_richcompare,		  /* tp_richcompare */
   0,				  /* tp_weaklistoffset */
-  0,				  /* tp_iter */
+  typy_iter,			  /* tp_iter */
   0,				  /* tp_iternext */
   type_object_methods,		  /* tp_methods */
   0,				  /* tp_members */
@@ -1221,3 +1535,35 @@
   0,				  /* tp_alloc */
   0,				  /* tp_new */
 };
+
+static PyTypeObject type_iterator_object_type = {
+  PyObject_HEAD_INIT (NULL)
+  0,				  /*ob_size*/
+  "gdb.TypeIterator",		  /*tp_name*/
+  sizeof (typy_iterator_object),  /*tp_basicsize*/
+  0,				  /*tp_itemsize*/
+  typy_iterator_dealloc,	  /*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_HAVE_ITER,  /*tp_flags*/
+  "GDB type iterator object",	  /*tp_doc */
+  0,				  /*tp_traverse */
+  0,				  /*tp_clear */
+  0,				  /*tp_richcompare */
+  0,				  /*tp_weaklistoffset */
+  typy_iterator_iter,             /*tp_iter */
+  typy_iterator_iternext,	  /*tp_iternext */
+  0				  /*tp_methods */
+};
Index: python/python-internal.h
===================================================================
RCS file: /cvs/src/src/gdb/python/python-internal.h,v
retrieving revision 1.48
diff -u -r1.48 python-internal.h
--- python/python-internal.h	15 Sep 2011 12:42:30 -0000	1.48
+++ python/python-internal.h	15 Sep 2011 18:34:41 -0000
@@ -104,6 +104,8 @@
 
 #include "exceptions.h"
 
+enum gdbpy_iter_kind { iter_keys, iter_values, iter_items };
+
 struct block;
 struct value;
 struct language_defn;
Index: testsuite/gdb.python/py-type.c
===================================================================
RCS file: /cvs/src/src/gdb/testsuite/gdb.python/py-type.c,v
retrieving revision 1.4
diff -u -r1.4 py-type.c
--- testsuite/gdb.python/py-type.c	1 Jan 2011 15:33:49 -0000	1.4
+++ testsuite/gdb.python/py-type.c	15 Sep 2011 18:34:42 -0000
@@ -43,6 +43,10 @@
 
 #endif
 
+enum E
+{ v1, v2, v3
+};
+
 int
 main ()
 {
@@ -56,9 +60,12 @@
   d.e = 3;
   d.f = 4;
 #endif
-
+  enum E e;
+  
   st.a = 3;
   st.b = 5;
 
+  e = v2;
+  
   return 0;      /* break to inspect struct and array.  */
 }
Index: testsuite/gdb.python/py-type.exp
===================================================================
RCS file: /cvs/src/src/gdb/testsuite/gdb.python/py-type.exp,v
retrieving revision 1.13
diff -u -r1.13 py-type.exp
--- testsuite/gdb.python/py-type.exp	26 Jul 2011 18:38:55 -0000	1.13
+++ testsuite/gdb.python/py-type.exp	15 Sep 2011 18:34:42 -0000
@@ -86,6 +86,14 @@
   gdb_test "python print fields\[0\].name" "a" "Check structure field a name"
   gdb_test "python print fields\[1\].name" "b" "Check structure field b name"
 
+  # Test Python mapping behavior of gdb.Type for structs/classes
+  gdb_test "python print len(st.type)" "2" "Check number of fields"
+  gdb_test "python print st.type\['a'\].name" "a" "Check fields lookup by name"
+    gdb_test "python print \[v.bitpos for v in st.type.itervalues()\]" {\[0L, 32L\]} "Check fields iteration over values"
+    gdb_test "python print \[(n, v.bitpos) for (n, v) in st.type.items()\]" {\[\('a', 0L\), \('b', 32L\)\]} "Check fields items list"
+  gdb_test "python print 'a' in st.type" "True" "Check field name exists test"
+  gdb_test "python print 'nosuch' in st.type" "False" "Check field name nonexists test"
+  
   # Test regression PR python/10805
   gdb_py_test_silent_cmd "print ar" "print value" 1
   gdb_py_test_silent_cmd "python ar = gdb.history (0)" "get value from  history" 1
@@ -101,6 +109,21 @@
   gdb_test "python print ar\[0\].type == ar\[0\].type" "True"
 }
 
+proc test_enums {} {
+  gdb_py_test_silent_cmd "print e" "print value" 1
+  gdb_py_test_silent_cmd "python e = gdb.history (0)" "get value from  history" 1
+  gdb_py_test_silent_cmd "python fields = e.type.fields()" "get value from history" 1
+  gdb_test "python print len(fields)" "3" "Check the number of enum fields"
+  gdb_test "python print fields\[0\].name" "v1" "Check enum field name"
+  gdb_test "python print fields\[1\].name" "v2" "Check enum field name"
+
+  # Ditto but by mapping operations
+  gdb_test "python print len(e.type)" "3" "Check the number of enum fields"
+  gdb_test "python print e.type\['v1'\].name" "v1" "Check enum field lookup by name"
+  gdb_test "python print e.type\['v3'\].name" "v3" "Check enum field lookup by name"
+    gdb_test "python print \[v.bitpos for v in e.type.itervalues()\]" {\[0L, 1L, 2L\]} "Check num fields iteration over values"
+    gdb_test "python print \[(n, v.bitpos) for (n, v) in e.type.items()\]" {\[\('v1', 0L\), \('v2', 1L\), \('v3', 2L\)\]} "Check enum fields items list"
+}
 proc test_base_class {} {
   gdb_py_test_silent_cmd "print d" "print value" 1
   gdb_py_test_silent_cmd "python d = gdb.history (0)" "get value from  history" 1
@@ -169,6 +192,7 @@
 
 runto_bp "break to inspect struct and array."
 test_fields "c"
+test_enums
 
 # Perform C++ Tests.
 build_inferior "${binfile}-cxx" "c++"
@@ -178,3 +202,4 @@
 test_base_class
 test_range
 test_template
+test_enums


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

* Re: Python: add field access by name and standard python mapping methods to gdb.Type
  2011-09-16  4:09 Python: add field access by name and standard python mapping methods to gdb.Type Paul Koning
@ 2011-09-16  7:36 ` Eli Zaretskii
  2011-09-16 10:47 ` Phil Muldoon
  1 sibling, 0 replies; 26+ messages in thread
From: Eli Zaretskii @ 2011-09-16  7:36 UTC (permalink / raw)
  To: Paul Koning; +Cc: gdb-patches

> From: Paul Koning <paulkoning@comcast.net>
> Date: Thu, 15 Sep 2011 16:38:33 -0400
> 
> The attached proposed patch adds access to struct and enum fields by their name, just as field names can be used on gdb.Value objects.  It also adds the standard Python methods for mappings (dictionary-like objects), so that the kind of things that Python programmers would expect to work on objects that can be indexed by name indeed do work.  For example, you can iterate through the fields, or query if a field name exists.  

Comments for the documentation part:

> +If the type is a structure or class type, or an enum type, the fields
> +of that type can be accessed using the Python @dfn{dictionary syntax}.
> +For example, if @code{some_type} is a @code{gdb.Type} instance holding
> +a structure type, you can access its @code{foo} element with:
                                                   ^^^^^^^
For consistency with preceding text, I suggest to use "field" here.

> +@code{bar} will be a @code{gdb.Field} object; see below under the
> +description of the @code{fields()} method for a description of the

"@code{fields}", without the parens.  If you use the parentheses, it
looks like a call to `fields' with no arguments, which is not what you
want.

In general, good Texinfo usage and GNU standards frown on the man-page
tradition of using "()" to indicate that the symbol is a function.

> +gdb.Field class.

Forgot @code here.

> +Each field is an @code{gdb.Field} object, with some pre-defined attributes:
                 ^^
"a"

Okay with those changes.

Thanks.


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

* Re: Python: add field access by name and standard python mapping methods to gdb.Type
  2011-09-16  4:09 Python: add field access by name and standard python mapping methods to gdb.Type Paul Koning
  2011-09-16  7:36 ` Eli Zaretskii
@ 2011-09-16 10:47 ` Phil Muldoon
  2011-09-16 14:57   ` Paul Koning
  2011-09-16 15:41   ` Paul Koning
  1 sibling, 2 replies; 26+ messages in thread
From: Phil Muldoon @ 2011-09-16 10:47 UTC (permalink / raw)
  To: Paul Koning; +Cc: gdb-patches

Paul Koning <paulkoning@comcast.net> writes:

> The attached proposed patch adds access to struct and enum fields by their name, just as field names can be used on gdb.Value objects.  It also adds the standard Python methods for mappings (dictionary-like objects), so that the kind of things that Python programmers would expect to work on objects that can be indexed by name indeed do work.  For example, you can iterate through the fields, or query if a field name exists.  
>
> For a change like this that adds a new feature which uses a whole pile
> of new functions, is the sort of ChangeLog entry shown below the right
> way to document the change, or should I write this in a different way?

Other than one small nit, your ChangeLog is perfectly acceptable.  I
think you should on the py-type.c line, list the functions first, then
the comment.

>  
> -/* Return a sequence of all fields.  Each field is a dictionary with
> -   some pre-defined keys.  */
> +/* Helper function to return the name of a field, as a Python object.
> */

"Or None." to document what happens if a name cannot be found.

>  static PyObject *
> -typy_fields (PyObject *self, PyObject *args)
> +field_name (struct type *type, int field)
>  {
>    PyObject *result;
> +
> +  if (TYPE_FIELD_NAME (type, field)) 
> +    result = PyString_FromString (TYPE_FIELD_NAME (type, field));
> +  else
> +    {
> +      result = Py_None;
> +      Py_INCREF (result);
> +    }
> +  return result;
> +}



> +static PyObject *
> +make_fielditem (struct type *type, int i, enum gdbpy_iter_kind kind)
> +{
> +  PyObject *item = NULL, *key = NULL, *value = NULL;
> +
> +  switch (kind)
> +    {
> +    case iter_items:
> +      key = field_name (type, i);
> +      if (key == NULL)
> +	goto fail;
> +      value = convert_field (type, i);
> +      if (value == NULL)
> +	goto fail;
> +      item = PyTuple_New (2);
> +      if (item == NULL)
> +	goto fail;
> +      PyTuple_SET_ITEM (item, 0, key);
> +      PyTuple_SET_ITEM (item, 1, value);
> +      break;
> +    case iter_keys:
> +      item = field_name (type, i);
> +      break;
> +    case iter_values:
> +      item =  convert_field (type, i);
> +      break;
> +    }
> +  return item;
> +  
> + fail:
> +  Py_XDECREF (key);
> +  Py_XDECREF (value);
> +  Py_XDECREF (item);
> +  return NULL;
> +}

Because this function returns a value that is not simple (it returns a
tuple) a brief comment on the return type and the contents would be
great. 

> +typy_getitem (PyObject *self, PyObject *key)
> +{
> +  struct type *type = ((type_object *) self)->type;
> +  char *field;
> +  int i;
> +  
> +  field = python_string_to_host_string (key);
> +  if (field == NULL)
> +    return NULL;
> +
> +  /* We want just fields of this type, not of base types, so instead of 
> +     using lookup_struct_elt_type, portions of that function are
> +     copied here.  */
> +
> +  for (;;)
> +    {
> +      CHECK_TYPEDEF (type);
> +      if (TYPE_CODE (type) != TYPE_CODE_PTR
> +	  && TYPE_CODE (type) != TYPE_CODE_REF)
> +	break;
> +      type = TYPE_TARGET_TYPE (type);
> +    }

This gives me pause, not because it is wrong, but because I wonder if
there is a possibility that this loop will never exit.  I presume it
will eventually find the base_type, just by continually walking the
TARGET_TYPE until it reaches bottom.  

Can you check how this is done in other parts of GDB (this must happen
quite often?).

> +typy_get (PyObject *self, PyObject *args)
> +{
> +  PyObject *key, *defval = Py_None, *result;
> +  
> +  if (!PyArg_UnpackTuple (args, "get", 1, 2, &key, &defval))
> +    return NULL;
> +  
> +  result = typy_getitem (self, key);
> +  if (result != NULL)
> +    return result;
> +  
> +  /* getitem returned error, clear the exception status and
> +     return the defval instead.  */
> +  PyErr_Clear ();

Given that typy_getitem can raise errors that do not relate directly to
a lookup (the string conversion call in there might indicate some
other conditions, like out of memory, for example), should we only clear
exceptions in some specific case?


> +/* Implement the "has_key" method on the type object.  */
> +static PyObject *
> +typy_has_key (PyObject *self, PyObject *args)
> +{
> +  struct type *type = ((type_object *) self)->type;
> +  char *field;
> +  int i;
> +  
> +  if (!PyArg_ParseTuple (args, "s", &field))
> +    return NULL;
> +
> +  /* We want just fields of this type, not of base types, so instead of 
> +     using lookup_struct_elt_type, portions of that function are
> +     copied here.  */
> +
> +  for (;;)
> +    {
> +      CHECK_TYPEDEF (type);
> +      if (TYPE_CODE (type) != TYPE_CODE_PTR
> +	  && TYPE_CODE (type) != TYPE_CODE_REF)
> +	break;
> +      type = TYPE_TARGET_TYPE (type);
> +    }

See above for the comment on this loop.  If GDB does not have a helper
macro to this already, you might consider just adding one.

Thanks for this patch, I think it is very useful

Phil


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

* Re: Python: add field access by name and standard python mapping methods to gdb.Type
  2011-09-16 10:47 ` Phil Muldoon
@ 2011-09-16 14:57   ` Paul Koning
  2011-09-16 14:57     ` Phil Muldoon
  2011-09-16 15:41   ` Paul Koning
  1 sibling, 1 reply; 26+ messages in thread
From: Paul Koning @ 2011-09-16 14:57 UTC (permalink / raw)
  To: pmuldoon; +Cc: gdb-patches


On Sep 16, 2011, at 6:45 AM, Phil Muldoon wrote:

> Paul Koning <paulkoning@comcast.net> writes:
> 
>> ...
> 
>> +typy_getitem (PyObject *self, PyObject *key)
>> +{
>> +  struct type *type = ((type_object *) self)->type;
>> +  char *field;
>> +  int i;
>> +  
>> +  field = python_string_to_host_string (key);
>> +  if (field == NULL)
>> +    return NULL;
>> +
>> +  /* We want just fields of this type, not of base types, so instead of 
>> +     using lookup_struct_elt_type, portions of that function are
>> +     copied here.  */
>> +
>> +  for (;;)
>> +    {
>> +      CHECK_TYPEDEF (type);
>> +      if (TYPE_CODE (type) != TYPE_CODE_PTR
>> +	  && TYPE_CODE (type) != TYPE_CODE_REF)
>> +	break;
>> +      type = TYPE_TARGET_TYPE (type);
>> +    }
> 
> This gives me pause, not because it is wrong, but because I wonder if
> there is a possibility that this loop will never exit.  I presume it
> will eventually find the base_type, just by continually walking the
> TARGET_TYPE until it reaches bottom.  
> 
> Can you check how this is done in other parts of GDB (this must happen
> quite often?).

This code was directly lifted from lookup_struct_elt_type in gdbtypes.c  The same sort of thing occurs in a number of other places, as you expected.  For example, in c_value_of_variable in varobj.c, a similar loop shows up but that one just strips TYPE_CODE_REF, it does not look for TYPE_CODE_PTR.

I can certainly make this a standard function, perhaps in gdbtypes.c.  Then I can also change other occurrences of this code pattern to call that, but I would not want to go use it for things that are similar but not identical (like the one in varobj.c I mentioned).  Should that be a separate patch?  It seems better that way.

	paul


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

* Re: Python: add field access by name and standard python mapping methods to gdb.Type
  2011-09-16 14:57   ` Paul Koning
@ 2011-09-16 14:57     ` Phil Muldoon
  0 siblings, 0 replies; 26+ messages in thread
From: Phil Muldoon @ 2011-09-16 14:57 UTC (permalink / raw)
  To: Paul Koning; +Cc: gdb-patches

Paul Koning <paulkoning@comcast.net> writes:

> On Sep 16, 2011, at 6:45 AM, Phil Muldoon wrote:
>
>> Paul Koning <paulkoning@comcast.net> writes:
>> 
>>> ...
>> 
>>> +typy_getitem (PyObject *self, PyObject *key)
>>> +{
>>> +  struct type *type = ((type_object *) self)->type;
>>> +  char *field;
>>> +  int i;
>>> +  
>>> +  field = python_string_to_host_string (key);
>>> +  if (field == NULL)
>>> +    return NULL;
>>> +
>>> +  /* We want just fields of this type, not of base types, so instead of 
>>> +     using lookup_struct_elt_type, portions of that function are
>>> +     copied here.  */
>>> +
>>> +  for (;;)
>>> +    {
>>> +      CHECK_TYPEDEF (type);
>>> +      if (TYPE_CODE (type) != TYPE_CODE_PTR
>>> +	  && TYPE_CODE (type) != TYPE_CODE_REF)
>>> +	break;
>>> +      type = TYPE_TARGET_TYPE (type);
>>> +    }
>> 
>> This gives me pause, not because it is wrong, but because I wonder if
>> there is a possibility that this loop will never exit.  I presume it
>> will eventually find the base_type, just by continually walking the
>> TARGET_TYPE until it reaches bottom.  
>> 
>> Can you check how this is done in other parts of GDB (this must happen
>> quite often?).
>
> This code was directly lifted from lookup_struct_elt_type in gdbtypes.c  The same sort of thing occurs in a number of other places, as you expected.  For example, in c_value_of_variable in varobj.c, a similar loop shows up but that one just strips TYPE_CODE_REF, it does not look for TYPE_CODE_PTR.
>
> I can certainly make this a standard function, perhaps in gdbtypes.c.  Then I can also change other occurrences of this code pattern to call that, but I would not want to go use it for things that are similar but not identical (like the one in varobj.c I mentioned).  Should that be a separate patch?  It seems better that way.
>
> 	paul

Yeah that is fine.  Thanks for checking!

Cheers

Phil


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

* Re: Python: add field access by name and standard python mapping methods to gdb.Type
  2011-09-16 10:47 ` Phil Muldoon
  2011-09-16 14:57   ` Paul Koning
@ 2011-09-16 15:41   ` Paul Koning
  2011-09-23 17:21     ` Doug Evans
  1 sibling, 1 reply; 26+ messages in thread
From: Paul Koning @ 2011-09-16 15:41 UTC (permalink / raw)
  To: gdb-patches, pmuldoon, Eli Zaretskii


On Sep 16, 2011, at 2:48 AM, Eli Zaretskii wrote:

>> From: Paul Koning <paulkoning@comcast.net>
>> Date: Thu, 15 Sep 2011 16:38:33 -0400
>> ...
> 
>> +@code{bar} will be a @code{gdb.Field} object; see below under the
>> +description of the @code{fields()} method for a description of the
> 
> "@code{fields}", without the parens.  If you use the parentheses, it
> looks like a call to `fields' with no arguments, which is not what you
> want.
> 
> In general, good Texinfo usage and GNU standards frown on the man-page
> tradition of using "()" to indicate that the symbol is a function.

I made it @code{Type.fields} to match the recent patch that uses explicit class names when referring to methods.

On Sep 16, 2011, at 6:45 AM, Phil Muldoon wrote:

> Paul Koning <paulkoning@comcast.net> writes:
> 
>> The attached proposed patch adds access to struct and enum fields by their name, just as field names can be used on gdb.Value objects.  It also adds the standard Python methods for mappings (dictionary-like objects), so that the kind of things that Python programmers would expect to work on objects that can be indexed by name indeed do work.  For example, you can iterate through the fields, or query if a field name exists.  
>> ...
> 
>> +typy_get (PyObject *self, PyObject *args)
>> +{
>> +  PyObject *key, *defval = Py_None, *result;
>> +  
>> +  if (!PyArg_UnpackTuple (args, "get", 1, 2, &key, &defval))
>> +    return NULL;
>> +  
>> +  result = typy_getitem (self, key);
>> +  if (result != NULL)
>> +    return result;
>> +  
>> +  /* getitem returned error, clear the exception status and
>> +     return the defval instead.  */
>> +  PyErr_Clear ();
> 
> Given that typy_getitem can raise errors that do not relate directly to
> a lookup (the string conversion call in there might indicate some
> other conditions, like out of memory, for example), should we only clear
> exceptions in some specific case?

Yes, that was wrong (and the Python C API book specifically mentions this so I should have caught that).

Below is an update with the various comments addressed.

	paul

2011-09-15  Paul Koning  <paul_koning@dell.com>

	* python/py-type.c (make_fielditem, typy_field_names, typy_items,
	typy_length, typy_get, typy_has_key, typy_make_iter,
	typy_iterkeys, typy_iteritems, typy_itervalues, typy_iter,
	typy_iterator_iter, typy_iterator_iternext,
	typy_iterator_dealloc): : New functions to implement standard
	Python mapping methods on gdb.Type object.
	(gdb.TypeIterator): New Python type.
	* python/python-internal.h (gdbpy_iter_kind): New enum.
	* doc/gdb.texinfo (gdb.Type): Document field access by dictionary
	key syntax.

2011-09-15  Paul Koning  <paul_koning@dell.com>

	* gdb.python/py-type.c (enum E): New.
	* gdb.python/py-type.exp (test_fields): Add tests for Python
	mapping access to fields.
	(test_enums): New test for field access on enums.


Index: doc/gdb.texinfo
===================================================================
RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v
retrieving revision 1.862
diff -u -r1.862 gdb.texinfo
--- doc/gdb.texinfo	16 Sep 2011 09:07:01 -0000	1.862
+++ doc/gdb.texinfo	16 Sep 2011 15:19:54 -0000
@@ -21537,6 +21537,19 @@
 If the named type cannot be found, it will throw an exception.
 @end defun
 
+If the type is a structure or class type, or an enum type, the fields
+of that type can be accessed using the Python @dfn{dictionary syntax}.
+For example, if @code{some_type} is a @code{gdb.Type} instance holding
+a structure type, you can access its @code{foo} field with:
+
+@smallexample
+bar = some_type['foo']
+@end smallexample
+
+@code{bar} will be a @code{gdb.Field} object; see below under the
+description of the @code{Type.fields} method for a description of the
+@code{gdb.Field} class.
+
 An instance of @code{Type} has the following attributes:
 
 @table @code
@@ -21570,7 +21583,7 @@
 represented as fields.  If the type has no fields, or does not fit
 into one of these categories, an empty sequence will be returned.
 
-Each field is an object, with some pre-defined attributes:
+Each field is a @code{gdb.Field} object, with some pre-defined attributes:
 @table @code
 @item bitpos
 This attribute is not available for @code{static} fields (as in
Index: python/py-type.c
===================================================================
RCS file: /cvs/src/src/gdb/python/py-type.c,v
retrieving revision 1.21
diff -u -r1.21 py-type.c
--- python/py-type.c	15 Sep 2011 18:33:15 -0000	1.21
+++ python/py-type.c	16 Sep 2011 15:19:54 -0000
@@ -55,6 +55,19 @@
 
 static PyTypeObject field_object_type;
 
+/* A type iterator object.  */
+typedef struct {
+  PyObject_HEAD
+  /* The current field index.  */
+  int field;
+  /* What to return.  */
+  enum gdbpy_iter_kind kind;
+  /* Pointer back to the original source type object.  */
+  struct pyty_type_object *source;
+} typy_iterator_object;
+
+static PyTypeObject type_iterator_object_type;
+
 /* This is used to initialize various gdb.TYPE_ constants.  */
 struct pyty_code
 {
@@ -210,12 +223,70 @@
   return NULL;
 }
 
-/* Return a sequence of all fields.  Each field is a dictionary with
-   some pre-defined keys.  */
+/* Helper function to return the name of a field, as a Python object.
+   If the field doesn't have a name, None is returned.  */
 static PyObject *
-typy_fields (PyObject *self, PyObject *args)
+field_name (struct type *type, int field)
 {
   PyObject *result;
+
+  if (TYPE_FIELD_NAME (type, field))
+    result = PyString_FromString (TYPE_FIELD_NAME (type, field));
+  else
+    {
+      result = Py_None;
+      Py_INCREF (result);
+    }
+  return result;
+}
+
+/* Helper function for Type standard mapping methods.  Returns a
+   Python object for field i of the type.  "kind" specifies what to
+   return: the name of the field, a gdb.Field object corresponding to
+   the field, or a tuple consisting of field name and gdb.Field
+   object.  */
+static PyObject *
+make_fielditem (struct type *type, int i, enum gdbpy_iter_kind kind)
+{
+  PyObject *item = NULL, *key = NULL, *value = NULL;
+
+  switch (kind)
+    {
+    case iter_items:
+      key = field_name (type, i);
+      if (key == NULL)
+	goto fail;
+      value = convert_field (type, i);
+      if (value == NULL)
+	goto fail;
+      item = PyTuple_New (2);
+      if (item == NULL)
+	goto fail;
+      PyTuple_SET_ITEM (item, 0, key);
+      PyTuple_SET_ITEM (item, 1, value);
+      break;
+    case iter_keys:
+      item = field_name (type, i);
+      break;
+    case iter_values:
+      item =  convert_field (type, i);
+      break;
+    }
+  return item;
+  
+ fail:
+  Py_XDECREF (key);
+  Py_XDECREF (value);
+  Py_XDECREF (item);
+  return NULL;
+}
+
+/* Return a sequence of all field names, fields, or (name, field) pairs.
+   Each field is a dictionary with some pre-defined keys.  */
+static PyObject *
+typy_fields_items (PyObject *self, enum gdbpy_iter_kind kind)
+{
+  PyObject *result = NULL, *item = NULL;
   int i;
   struct type *type = ((type_object *) self)->type;
   volatile struct gdb_exception except;
@@ -230,26 +301,49 @@
      then memoize the result (and perhaps make Field.type() lazy).
      However, that can lead to cycles.  */
   result = PyList_New (0);
-
+  if (result == NULL)
+    return NULL;
+  
   for (i = 0; i < TYPE_NFIELDS (type); ++i)
     {
-      PyObject *dict = convert_field (type, i);
-
-      if (!dict)
-	{
-	  Py_DECREF (result);
-	  return NULL;
-	}
-      if (PyList_Append (result, dict))
-	{
-	  Py_DECREF (dict);
-	  Py_DECREF (result);
-	  return NULL;
-	}
-      Py_DECREF (dict);
+      item = make_fielditem (type, i, kind);
+      if (!item)
+	goto fail;
+      if (PyList_Append (result, item))
+	goto fail;
+      Py_DECREF (item);
     }
 
   return result;
+
+ fail:
+  Py_XDECREF (item);
+  Py_XDECREF (result);
+  return NULL;
+}
+
+/* Return a sequence of all fields.  Each field is a dictionary with
+   some pre-defined keys.  */
+static PyObject *
+typy_fields (PyObject *self, PyObject *args)
+{
+  return typy_fields_items (self, iter_values);
+}
+
+/* Return a sequence of all field names.  Each field is a dictionary with
+   some pre-defined keys.  */
+static PyObject *
+typy_field_names (PyObject *self, PyObject *args)
+{
+  return typy_fields_items (self, iter_keys);
+}
+
+/* Return a sequence of all (name, fields) pairs.  Each field is a 
+   dictionary with some pre-defined keys.  */
+static PyObject *
+typy_items (PyObject *self, PyObject *args)
+{
+  return typy_fields_items (self, iter_items);
 }
 
 /* Return the type's tag, or None.  */
@@ -1000,6 +1094,198 @@
   type->ob_type->tp_free (type);
 }
 
+/* Return number of fields ("length" of the field dictionary).  */
+static Py_ssize_t
+typy_length (PyObject *self)
+{
+  struct type *type = ((type_object *) self)->type;
+
+  return TYPE_NFIELDS (type);
+}
+
+/* Return a field object for the field named by the argument.  */
+static PyObject *
+typy_getitem (PyObject *self, PyObject *key)
+{
+  struct type *type = ((type_object *) self)->type;
+  char *field;
+  int i;
+  
+  field = python_string_to_host_string (key);
+  if (field == NULL)
+    return NULL;
+
+  /* We want just fields of this type, not of base types, so instead of 
+     using lookup_struct_elt_type, portions of that function are
+     copied here.  */
+
+  for (;;)
+    {
+      CHECK_TYPEDEF (type);
+      if (TYPE_CODE (type) != TYPE_CODE_PTR
+	  && TYPE_CODE (type) != TYPE_CODE_REF)
+	break;
+      type = TYPE_TARGET_TYPE (type);
+    }
+
+  for (i = 0; i < TYPE_NFIELDS (type); i++)
+    {
+      char *t_field_name = TYPE_FIELD_NAME (type, i);
+
+      if (t_field_name && (strcmp_iw (t_field_name, field) == 0))
+	{
+	  return convert_field (type, i);
+	}
+    }
+  PyErr_SetObject (PyExc_KeyError, key);
+  return NULL;
+}
+
+/* Implement the "get" method on the type object.  This is the 
+   same as getitem if the key is present, but returns the supplied
+   default value or None if the key is not found.  */
+static PyObject *
+typy_get (PyObject *self, PyObject *args)
+{
+  PyObject *key, *defval = Py_None, *result;
+  
+  if (!PyArg_UnpackTuple (args, "get", 1, 2, &key, &defval))
+    return NULL;
+  
+  result = typy_getitem (self, key);
+  if (result != NULL)
+    return result;
+  
+  /* typy_getitem returned error status.  If the exception is
+     KeyError, clear the exception status and return the defval
+     instead.  Otherwise return the exception unchanged.  */
+  if (!PyErr_ExceptionMatches (PyExc_KeyError))
+    return NULL;
+  
+  PyErr_Clear ();
+  Py_INCREF (defval);
+  return defval;
+}
+
+/* Implement the "has_key" method on the type object.  */
+static PyObject *
+typy_has_key (PyObject *self, PyObject *args)
+{
+  struct type *type = ((type_object *) self)->type;
+  char *field;
+  int i;
+  
+  if (!PyArg_ParseTuple (args, "s", &field))
+    return NULL;
+
+  /* We want just fields of this type, not of base types, so instead of 
+     using lookup_struct_elt_type, portions of that function are
+     copied here.  */
+
+  for (;;)
+    {
+      CHECK_TYPEDEF (type);
+      if (TYPE_CODE (type) != TYPE_CODE_PTR
+	  && TYPE_CODE (type) != TYPE_CODE_REF)
+	break;
+      type = TYPE_TARGET_TYPE (type);
+    }
+
+  for (i = 0; i < TYPE_NFIELDS (type); i++)
+    {
+      char *t_field_name = TYPE_FIELD_NAME (type, i);
+
+      if (t_field_name && (strcmp_iw (t_field_name, field) == 0))
+	Py_RETURN_TRUE;
+    }
+  Py_RETURN_FALSE;
+}
+
+/* Make an iterator object to iterate over keys, values, or items.  */
+static PyObject *
+typy_make_iter (PyObject *self, enum gdbpy_iter_kind kind)
+{
+  typy_iterator_object *typy_iter_obj;
+
+  typy_iter_obj = PyObject_New (typy_iterator_object,
+				&type_iterator_object_type);
+  if (typy_iter_obj == NULL)
+      return NULL;
+
+  typy_iter_obj->field = 0;
+  typy_iter_obj->kind = kind;
+  Py_INCREF (self);
+  typy_iter_obj->source = (type_object *) self;
+
+  return (PyObject *) typy_iter_obj;
+}
+
+/* iteritems() method.  */
+static PyObject *
+typy_iteritems (PyObject *self, PyObject *args)
+{
+  return typy_make_iter (self, iter_items);
+}
+
+/* iterkeys() method.  */
+static PyObject *
+typy_iterkeys (PyObject *self, PyObject *args)
+{
+  return typy_make_iter (self, iter_keys);
+}
+
+/* Iterating over the class, same as iterkeys
+   except for the function signature.  */
+static PyObject *
+typy_iter (PyObject *self)
+{
+  return typy_make_iter (self, iter_keys);
+}
+
+/* itervalues() method.  */
+static PyObject *
+typy_itervalues (PyObject *self, PyObject *args)
+{
+  return typy_make_iter (self, iter_values);
+}
+
+/* Return a reference to the type iterator.  */
+static PyObject *
+typy_iterator_iter (PyObject *self)
+{
+  Py_INCREF (self);
+  return self;
+}
+
+/* Return the next field in the iteration through the list of fields
+   of the type.  */
+static PyObject *
+typy_iterator_iternext (PyObject *self)
+{
+  typy_iterator_object *iter_obj = (typy_iterator_object *) self;
+  struct type *type = iter_obj->source->type;
+  int i;
+  PyObject *result;
+  
+  if (iter_obj->field < TYPE_NFIELDS (type))
+    {
+      result = make_fielditem (type, iter_obj->field, iter_obj->kind);
+      if (result != NULL)
+	iter_obj->field++;
+      return result;
+    }
+
+  return NULL;
+}
+
+static void
+typy_iterator_dealloc (PyObject *obj)
+{
+  typy_iterator_object *iter_obj = (typy_iterator_object *) obj;
+
+  Py_DECREF (iter_obj->source);
+}
+
 /* Create a new Type referring to TYPE.  */
 PyObject *
 type_to_type_object (struct type *type)
@@ -1067,6 +1353,8 @@
     return;
   if (PyType_Ready (&field_object_type) < 0)
     return;
+  if (PyType_Ready (&type_iterator_object_type) < 0)
+    return;
 
   for (i = 0; pyty_codes[i].name; ++i)
     {
@@ -1080,6 +1368,10 @@
   Py_INCREF (&type_object_type);
   PyModule_AddObject (gdb_module, "Type", (PyObject *) &type_object_type);
 
+  Py_INCREF (&type_iterator_object_type);
+  PyModule_AddObject (gdb_module, "TypeIterator",
+		      (PyObject *) &type_iterator_object_type);
+
   Py_INCREF (&field_object_type);
   PyModule_AddObject (gdb_module, "Field", (PyObject *) &field_object_type);
 }
@@ -1102,13 +1394,35 @@
   { "array", typy_array, METH_VARARGS,
     "array (N) -> Type\n\
 Return a type which represents an array of N objects of this type." },
+   { "__contains__", typy_has_key, METH_VARARGS,
+     "T.__contains__(k) -> True if T has a field named k, else False" },
   { "const", typy_const, METH_NOARGS,
     "const () -> Type\n\
 Return a const variant of this type." },
   { "fields", typy_fields, METH_NOARGS,
-    "field () -> list\n\
-Return a sequence holding all the fields of this type.\n\
-Each field is a dictionary." },
+    "fields () -> list\n\
+Return a list holding all the fields of this type.\n\
+Each field is a gdb.Field object." },
+  { "get", typy_get, METH_VARARGS,
+    "T.get(k[,default]) -> returns field named k in T, if it exists;\n\
+otherwise returns default, if supplied, or None if not." },
+  { "has_key", typy_has_key, METH_VARARGS,
+    "T.has_key(k) -> True if T has a field named k, else False" },
+  { "items", typy_items, METH_NOARGS,
+    "items () -> list\n\
+Return a list of (name, field) pairs of this type.\n\
+Each field is a gdb.Field object." },
+  { "iteritems", typy_iteritems, METH_NOARGS,
+    "iteritems () -> an iterator over the (name, field)\n\
+pairs of this type.  Each field is a gdb.Field object." },
+  { "iterkeys", typy_iterkeys, METH_NOARGS,
+    "iterkeys () -> an iterator over the field names of this type." },
+  { "itervalues", typy_itervalues, METH_NOARGS,
+    "itervalues () -> an iterator over the fields of this type.\n\
+Each field is a gdb.Field object." },
+  { "keys", typy_field_names, METH_NOARGS,
+    "keys () -> list\n\
+Return a list holding all the fields names of this type." },
   { "pointer", typy_pointer, METH_NOARGS,
     "pointer () -> Type\n\
 Return a type of pointer to this type." },
@@ -1130,12 +1444,22 @@
   { "unqualified", typy_unqualified, METH_NOARGS,
     "unqualified () -> Type\n\
 Return a variant of this type without const or volatile attributes." },
+  { "values", typy_fields, METH_NOARGS,
+    "values () -> list\n\
+Return a list holding all the fields of this type.\n\
+Each field is a gdb.Field object." },
   { "volatile", typy_volatile, METH_NOARGS,
     "volatile () -> Type\n\
 Return a volatile variant of this type" },
   { NULL }
 };
 
+static PyMappingMethods typy_mapping = {
+  typy_length,
+  typy_getitem,
+  NULL				  /* no "set" method */
+};
+
 static PyTypeObject type_object_type =
 {
   PyObject_HEAD_INIT (NULL)
@@ -1151,7 +1475,7 @@
   0,				  /*tp_repr*/
   0,				  /*tp_as_number*/
   0,				  /*tp_as_sequence*/
-  0,				  /*tp_as_mapping*/
+  &typy_mapping,		  /*tp_as_mapping*/
   0,				  /*tp_hash */
   0,				  /*tp_call*/
   typy_str,			  /*tp_str*/
@@ -1164,7 +1488,7 @@
   0,				  /* tp_clear */
   typy_richcompare,		  /* tp_richcompare */
   0,				  /* tp_weaklistoffset */
-  0,				  /* tp_iter */
+  typy_iter,			  /* tp_iter */
   0,				  /* tp_iternext */
   type_object_methods,		  /* tp_methods */
   0,				  /* tp_members */
@@ -1221,3 +1545,35 @@
   0,				  /* tp_alloc */
   0,				  /* tp_new */
 };
+
+static PyTypeObject type_iterator_object_type = {
+  PyObject_HEAD_INIT (NULL)
+  0,				  /*ob_size*/
+  "gdb.TypeIterator",		  /*tp_name*/
+  sizeof (typy_iterator_object),  /*tp_basicsize*/
+  0,				  /*tp_itemsize*/
+  typy_iterator_dealloc,	  /*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_HAVE_ITER,  /*tp_flags*/
+  "GDB type iterator object",	  /*tp_doc */
+  0,				  /*tp_traverse */
+  0,				  /*tp_clear */
+  0,				  /*tp_richcompare */
+  0,				  /*tp_weaklistoffset */
+  typy_iterator_iter,             /*tp_iter */
+  typy_iterator_iternext,	  /*tp_iternext */
+  0				  /*tp_methods */
+};
Index: python/python-internal.h
===================================================================
RCS file: /cvs/src/src/gdb/python/python-internal.h,v
retrieving revision 1.48
diff -u -r1.48 python-internal.h
--- python/python-internal.h	15 Sep 2011 12:42:30 -0000	1.48
+++ python/python-internal.h	16 Sep 2011 15:19:54 -0000
@@ -104,6 +104,8 @@
 
 #include "exceptions.h"
 
+enum gdbpy_iter_kind { iter_keys, iter_values, iter_items };
+
 struct block;
 struct value;
 struct language_defn;
Index: testsuite/gdb.python/py-type.c
===================================================================
RCS file: /cvs/src/src/gdb/testsuite/gdb.python/py-type.c,v
retrieving revision 1.4
diff -u -r1.4 py-type.c
--- testsuite/gdb.python/py-type.c	1 Jan 2011 15:33:49 -0000	1.4
+++ testsuite/gdb.python/py-type.c	16 Sep 2011 15:19:54 -0000
@@ -43,6 +43,10 @@
 
 #endif
 
+enum E
+{ v1, v2, v3
+};
+
 int
 main ()
 {
@@ -56,9 +60,12 @@
   d.e = 3;
   d.f = 4;
 #endif
-
+  enum E e;
+  
   st.a = 3;
   st.b = 5;
 
+  e = v2;
+  
   return 0;      /* break to inspect struct and array.  */
 }
Index: testsuite/gdb.python/py-type.exp
===================================================================
RCS file: /cvs/src/src/gdb/testsuite/gdb.python/py-type.exp,v
retrieving revision 1.13
diff -u -r1.13 py-type.exp
--- testsuite/gdb.python/py-type.exp	26 Jul 2011 18:38:55 -0000	1.13
+++ testsuite/gdb.python/py-type.exp	16 Sep 2011 15:19:54 -0000
@@ -86,6 +86,14 @@
   gdb_test "python print fields\[0\].name" "a" "Check structure field a name"
   gdb_test "python print fields\[1\].name" "b" "Check structure field b name"
 
+  # Test Python mapping behavior of gdb.Type for structs/classes
+  gdb_test "python print len(st.type)" "2" "Check number of fields"
+  gdb_test "python print st.type\['a'\].name" "a" "Check fields lookup by name"
+    gdb_test "python print \[v.bitpos for v in st.type.itervalues()\]" {\[0L, 32L\]} "Check fields iteration over values"
+    gdb_test "python print \[(n, v.bitpos) for (n, v) in st.type.items()\]" {\[\('a', 0L\), \('b', 32L\)\]} "Check fields items list"
+  gdb_test "python print 'a' in st.type" "True" "Check field name exists test"
+  gdb_test "python print 'nosuch' in st.type" "False" "Check field name nonexists test"
+  
   # Test regression PR python/10805
   gdb_py_test_silent_cmd "print ar" "print value" 1
   gdb_py_test_silent_cmd "python ar = gdb.history (0)" "get value from  history" 1
@@ -101,6 +109,21 @@
   gdb_test "python print ar\[0\].type == ar\[0\].type" "True"
 }
 
+proc test_enums {} {
+  gdb_py_test_silent_cmd "print e" "print value" 1
+  gdb_py_test_silent_cmd "python e = gdb.history (0)" "get value from  history" 1
+  gdb_py_test_silent_cmd "python fields = e.type.fields()" "get value from history" 1
+  gdb_test "python print len(fields)" "3" "Check the number of enum fields"
+  gdb_test "python print fields\[0\].name" "v1" "Check enum field name"
+  gdb_test "python print fields\[1\].name" "v2" "Check enum field name"
+
+  # Ditto but by mapping operations
+  gdb_test "python print len(e.type)" "3" "Check the number of enum fields"
+  gdb_test "python print e.type\['v1'\].name" "v1" "Check enum field lookup by name"
+  gdb_test "python print e.type\['v3'\].name" "v3" "Check enum field lookup by name"
+    gdb_test "python print \[v.bitpos for v in e.type.itervalues()\]" {\[0L, 1L, 2L\]} "Check num fields iteration over values"
+    gdb_test "python print \[(n, v.bitpos) for (n, v) in e.type.items()\]" {\[\('v1', 0L\), \('v2', 1L\), \('v3', 2L\)\]} "Check enum fields items list"
+}
 proc test_base_class {} {
   gdb_py_test_silent_cmd "print d" "print value" 1
   gdb_py_test_silent_cmd "python d = gdb.history (0)" "get value from  history" 1
@@ -169,6 +192,7 @@
 
 runto_bp "break to inspect struct and array."
 test_fields "c"
+test_enums
 
 # Perform C++ Tests.
 build_inferior "${binfile}-cxx" "c++"
@@ -178,3 +202,4 @@
 test_base_class
 test_range
 test_template
+test_enums


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

* Re: Python: add field access by name and standard python mapping methods to gdb.Type
  2011-09-16 15:41   ` Paul Koning
@ 2011-09-23 17:21     ` Doug Evans
  2011-09-26 17:15       ` [RFA] " Paul Koning
  0 siblings, 1 reply; 26+ messages in thread
From: Doug Evans @ 2011-09-23 17:21 UTC (permalink / raw)
  To: Paul Koning; +Cc: gdb-patches, pmuldoon, Eli Zaretskii

On Fri, Sep 16, 2011 at 8:27 AM, Paul Koning <paulkoning@comcast.net> wrote:
>
> Below is an update with the various comments addressed.

Hi.  This email is for just a couple of formatting nits.

> 2011-09-15  Paul Koning  <paul_koning@dell.com>
>
>        * python/py-type.c (make_fielditem, typy_field_names, typy_items,
>        typy_length, typy_get, typy_has_key, typy_make_iter,
>        typy_iterkeys, typy_iteritems, typy_itervalues, typy_iter,
>        typy_iterator_iter, typy_iterator_iternext,
>        typy_iterator_dealloc): : New functions to implement standard
>        Python mapping methods on gdb.Type object.
>        (gdb.TypeIterator): New Python type.
>        * python/python-internal.h (gdbpy_iter_kind): New enum.
>        * doc/gdb.texinfo (gdb.Type): Document field access by dictionary
>        key syntax.

Technically speaking, the rule is you can't extend a parenthesized name list
over multiple lines.  At least that's the rule I've been asked to follow.
I'd like to see some flexibility here, and I'm not asking you change this here.
Just pointing it out.

> +/* Helper function for Type standard mapping methods.  Returns a
> +   Python object for field i of the type.  "kind" specifies what to
> +   return: the name of the field, a gdb.Field object corresponding to
> +   the field, or a tuple consisting of field name and gdb.Field
> +   object.  */
> +static PyObject *
> +make_fielditem (struct type *type, int i, enum gdbpy_iter_kind kind)

For functions the coding standards say to have a blank line between
its comment and definition.
I realize parts of gdb don't follow this rule, but this rule I like :-),
it is documented, and I'd like to not make things worse.

This needs to be fixed in several places.


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

* [RFA] Re: Python: add field access by name and standard python mapping methods to gdb.Type
  2011-09-23 17:21     ` Doug Evans
@ 2011-09-26 17:15       ` Paul Koning
  2011-09-28 19:25         ` Doug Evans
  0 siblings, 1 reply; 26+ messages in thread
From: Paul Koning @ 2011-09-26 17:15 UTC (permalink / raw)
  To: Doug Evans; +Cc: gdb-patches


On Sep 23, 2011, at 12:33 PM, Doug Evans wrote:

> On Fri, Sep 16, 2011 at 8:27 AM, Paul Koning <paulkoning@comcast.net> wrote:
>> 
>> Below is an update with the various comments addressed.
> 
> Hi.  This email is for just a couple of formatting nits.
> 
>> 2011-09-15  Paul Koning  <paul_koning@dell.com>
>> 
>>        * python/py-type.c (make_fielditem, typy_field_names, typy_items,
>>        typy_length, typy_get, typy_has_key, typy_make_iter,
>>        typy_iterkeys, typy_iteritems, typy_itervalues, typy_iter,
>>        typy_iterator_iter, typy_iterator_iternext,
>>        typy_iterator_dealloc): : New functions to implement standard
>>        Python mapping methods on gdb.Type object.
>>        (gdb.TypeIterator): New Python type.
>>        * python/python-internal.h (gdbpy_iter_kind): New enum.
>>        * doc/gdb.texinfo (gdb.Type): Document field access by dictionary
>>        key syntax.
> 
> Technically speaking, the rule is you can't extend a parenthesized name list
> over multiple lines.  At least that's the rule I've been asked to follow.
> I'd like to see some flexibility here, and I'm not asking you change this here.
> Just pointing it out.
> 
>> +/* Helper function for Type standard mapping methods.  Returns a
>> +   Python object for field i of the type.  "kind" specifies what to
>> +   return: the name of the field, a gdb.Field object corresponding to
>> +   the field, or a tuple consisting of field name and gdb.Field
>> +   object.  */
>> +static PyObject *
>> +make_fielditem (struct type *type, int i, enum gdbpy_iter_kind kind)
> 
> For functions the coding standards say to have a blank line between
> its comment and definition.
> I realize parts of gdb don't follow this rule, but this rule I like :-),
> it is documented, and I'd like to not make things worse.
> 
> This needs to be fixed in several places.

I've fixed both -- the ChangeLog is now formatted according to an earlier example I see in ChangeLog.

Ok to commit?

	paul

2011-09-26  Paul Koning  <paul_koning@dell.com>

	* python/py-type.c (make_fielditem, typy_field_names, typy_items)
	(typy_length, typy_get, typy_has_key, typy_make_iter)
	(typy_iterkeys, typy_iteritems, typy_itervalues, typy_iter)
	(typy_iterator_iter, typy_iterator_iternext)
	(typy_iterator_dealloc): : New functions to implement standard
	Python mapping methods on gdb.Type object.
	(gdb.TypeIterator): New Python type.
	* python/python-internal.h (gdbpy_iter_kind): New enum.
	* doc/gdb.texinfo (gdb.Type): Document field access by dictionary
	key syntax.

2011-09-26  Paul Koning  <paul_koning@dell.com>

	* gdb.python/py-type.c (enum E): New.
	* gdb.python/py-type.exp (test_fields): Add tests for Python
	mapping access to fields.
	(test_enums): New test for field access on enums.

Index: doc/gdb.texinfo
===================================================================
RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v
retrieving revision 1.862
diff -u -r1.862 gdb.texinfo
--- doc/gdb.texinfo	16 Sep 2011 09:07:01 -0000	1.862
+++ doc/gdb.texinfo	26 Sep 2011 15:54:34 -0000
@@ -21537,6 +21537,19 @@
 If the named type cannot be found, it will throw an exception.
 @end defun
 
+If the type is a structure or class type, or an enum type, the fields
+of that type can be accessed using the Python @dfn{dictionary syntax}.
+For example, if @code{some_type} is a @code{gdb.Type} instance holding
+a structure type, you can access its @code{foo} field with:
+
+@smallexample
+bar = some_type['foo']
+@end smallexample
+
+@code{bar} will be a @code{gdb.Field} object; see below under the
+description of the @code{Type.fields} method for a description of the
+@code{gdb.Field} class.
+
 An instance of @code{Type} has the following attributes:
 
 @table @code
@@ -21570,7 +21583,7 @@
 represented as fields.  If the type has no fields, or does not fit
 into one of these categories, an empty sequence will be returned.
 
-Each field is an object, with some pre-defined attributes:
+Each field is a @code{gdb.Field} object, with some pre-defined attributes:
 @table @code
 @item bitpos
 This attribute is not available for @code{static} fields (as in
Index: python/py-type.c
===================================================================
RCS file: /cvs/src/src/gdb/python/py-type.c,v
retrieving revision 1.21
diff -u -r1.21 py-type.c
--- python/py-type.c	15 Sep 2011 18:33:15 -0000	1.21
+++ python/py-type.c	26 Sep 2011 15:54:34 -0000
@@ -55,6 +55,19 @@
 
 static PyTypeObject field_object_type;
 
+/* A type iterator object.  */
+typedef struct {
+  PyObject_HEAD
+  /* The current field index.  */
+  int field;
+  /* What to return.  */
+  enum gdbpy_iter_kind kind;
+  /* Pointer back to the original source type object.  */
+  struct pyty_type_object *source;
+} typy_iterator_object;
+
+static PyTypeObject type_iterator_object_type;
+
 /* This is used to initialize various gdb.TYPE_ constants.  */
 struct pyty_code
 {
@@ -137,7 +150,8 @@
 }
 
 /* Helper function for typy_fields which converts a single field to a
-   dictionary.  Returns NULL on error.  */
+   gdb.Field object.  Returns NULL on error.  */
+
 static PyObject *
 convert_field (struct type *type, int field)
 {
@@ -210,12 +224,73 @@
   return NULL;
 }
 
-/* Return a sequence of all fields.  Each field is a dictionary with
-   some pre-defined keys.  */
+/* Helper function to return the name of a field, as a gdb.Field object.
+   If the field doesn't have a name, None is returned.  */
+
 static PyObject *
-typy_fields (PyObject *self, PyObject *args)
+field_name (struct type *type, int field)
 {
   PyObject *result;
+
+  if (TYPE_FIELD_NAME (type, field))
+    result = PyString_FromString (TYPE_FIELD_NAME (type, field));
+  else
+    {
+      result = Py_None;
+      Py_INCREF (result);
+    }
+  return result;
+}
+
+/* Helper function for Type standard mapping methods.  Returns a
+   Python object for field i of the type.  "kind" specifies what to
+   return: the name of the field, a gdb.Field object corresponding to
+   the field, or a tuple consisting of field name and gdb.Field
+   object.  */
+
+static PyObject *
+make_fielditem (struct type *type, int i, enum gdbpy_iter_kind kind)
+{
+  PyObject *item = NULL, *key = NULL, *value = NULL;
+
+  switch (kind)
+    {
+    case iter_items:
+      key = field_name (type, i);
+      if (key == NULL)
+	goto fail;
+      value = convert_field (type, i);
+      if (value == NULL)
+	goto fail;
+      item = PyTuple_New (2);
+      if (item == NULL)
+	goto fail;
+      PyTuple_SET_ITEM (item, 0, key);
+      PyTuple_SET_ITEM (item, 1, value);
+      break;
+    case iter_keys:
+      item = field_name (type, i);
+      break;
+    case iter_values:
+      item =  convert_field (type, i);
+      break;
+    }
+  return item;
+  
+ fail:
+  Py_XDECREF (key);
+  Py_XDECREF (value);
+  Py_XDECREF (item);
+  return NULL;
+}
+
+/* Return a sequence of all field names, fields, or (name, field) pairs.
+   Each field is a gdb.Field object.  */
+
+static PyObject *
+typy_fields_items (PyObject *self, enum gdbpy_iter_kind kind)
+{
+  PyObject *result = NULL, *item = NULL;
   int i;
   struct type *type = ((type_object *) self)->type;
   volatile struct gdb_exception except;
@@ -230,26 +305,50 @@
      then memoize the result (and perhaps make Field.type() lazy).
      However, that can lead to cycles.  */
   result = PyList_New (0);
-
+  if (result == NULL)
+    return NULL;
+  
   for (i = 0; i < TYPE_NFIELDS (type); ++i)
     {
-      PyObject *dict = convert_field (type, i);
-
-      if (!dict)
-	{
-	  Py_DECREF (result);
-	  return NULL;
-	}
-      if (PyList_Append (result, dict))
-	{
-	  Py_DECREF (dict);
-	  Py_DECREF (result);
-	  return NULL;
-	}
-      Py_DECREF (dict);
+      item = make_fielditem (type, i, kind);
+      if (!item)
+	goto fail;
+      if (PyList_Append (result, item))
+	goto fail;
+      Py_DECREF (item);
     }
 
   return result;
+
+ fail:
+  Py_XDECREF (item);
+  Py_XDECREF (result);
+  return NULL;
+}
+
+/* Return a sequence of all fields.  Each field is a gdb.Field object.  */
+
+static PyObject *
+typy_fields (PyObject *self, PyObject *args)
+{
+  return typy_fields_items (self, iter_values);
+}
+
+/* Return a sequence of all field names.  Each field is a gdb.Field object.  */
+
+static PyObject *
+typy_field_names (PyObject *self, PyObject *args)
+{
+  return typy_fields_items (self, iter_keys);
+}
+
+/* Return a sequence of all (name, fields) pairs.  Each field is a 
+   gdb.Field object.  */
+
+static PyObject *
+typy_items (PyObject *self, PyObject *args)
+{
+  return typy_fields_items (self, iter_items);
 }
 
 /* Return the type's tag, or None.  */
@@ -1000,6 +1099,209 @@
   type->ob_type->tp_free (type);
 }
 
+/* Return number of fields ("length" of the field dictionary).  */
+
+static Py_ssize_t
+typy_length (PyObject *self)
+{
+  struct type *type = ((type_object *) self)->type;
+
+  return TYPE_NFIELDS (type);
+}
+
+/* Return a gdb.Field object for the field named by the argument.  */
+
+static PyObject *
+typy_getitem (PyObject *self, PyObject *key)
+{
+  struct type *type = ((type_object *) self)->type;
+  char *field;
+  int i;
+  
+  field = python_string_to_host_string (key);
+  if (field == NULL)
+    return NULL;
+
+  /* We want just fields of this type, not of base types, so instead of 
+     using lookup_struct_elt_type, portions of that function are
+     copied here.  */
+
+  for (;;)
+    {
+      CHECK_TYPEDEF (type);
+      if (TYPE_CODE (type) != TYPE_CODE_PTR
+	  && TYPE_CODE (type) != TYPE_CODE_REF)
+	break;
+      type = TYPE_TARGET_TYPE (type);
+    }
+
+  for (i = 0; i < TYPE_NFIELDS (type); i++)
+    {
+      char *t_field_name = TYPE_FIELD_NAME (type, i);
+
+      if (t_field_name && (strcmp_iw (t_field_name, field) == 0))
+	{
+	  return convert_field (type, i);
+	}
+    }
+  PyErr_SetObject (PyExc_KeyError, key);
+  return NULL;
+}
+
+/* Implement the "get" method on the type object.  This is the 
+   same as getitem if the key is present, but returns the supplied
+   default value or None if the key is not found.  */
+
+static PyObject *
+typy_get (PyObject *self, PyObject *args)
+{
+  PyObject *key, *defval = Py_None, *result;
+  
+  if (!PyArg_UnpackTuple (args, "get", 1, 2, &key, &defval))
+    return NULL;
+  
+  result = typy_getitem (self, key);
+  if (result != NULL)
+    return result;
+  
+  /* typy_getitem returned error status.  If the exception is
+     KeyError, clear the exception status and return the defval
+     instead.  Otherwise return the exception unchanged.  */
+  if (!PyErr_ExceptionMatches (PyExc_KeyError))
+    return NULL;
+  
+  PyErr_Clear ();
+  Py_INCREF (defval);
+  return defval;
+}
+
+/* Implement the "has_key" method on the type object.  */
+
+static PyObject *
+typy_has_key (PyObject *self, PyObject *args)
+{
+  struct type *type = ((type_object *) self)->type;
+  char *field;
+  int i;
+  
+  if (!PyArg_ParseTuple (args, "s", &field))
+    return NULL;
+
+  /* We want just fields of this type, not of base types, so instead of 
+     using lookup_struct_elt_type, portions of that function are
+     copied here.  */
+
+  for (;;)
+    {
+      CHECK_TYPEDEF (type);
+      if (TYPE_CODE (type) != TYPE_CODE_PTR
+	  && TYPE_CODE (type) != TYPE_CODE_REF)
+	break;
+      type = TYPE_TARGET_TYPE (type);
+    }
+
+  for (i = 0; i < TYPE_NFIELDS (type); i++)
+    {
+      char *t_field_name = TYPE_FIELD_NAME (type, i);
+
+      if (t_field_name && (strcmp_iw (t_field_name, field) == 0))
+	Py_RETURN_TRUE;
+    }
+  Py_RETURN_FALSE;
+}
+
+/* Make an iterator object to iterate over keys, values, or items.  */
+
+static PyObject *
+typy_make_iter (PyObject *self, enum gdbpy_iter_kind kind)
+{
+  typy_iterator_object *typy_iter_obj;
+
+  typy_iter_obj = PyObject_New (typy_iterator_object,
+				&type_iterator_object_type);
+  if (typy_iter_obj == NULL)
+      return NULL;
+
+  typy_iter_obj->field = 0;
+  typy_iter_obj->kind = kind;
+  Py_INCREF (self);
+  typy_iter_obj->source = (type_object *) self;
+
+  return (PyObject *) typy_iter_obj;
+}
+
+/* iteritems() method.  */
+
+static PyObject *
+typy_iteritems (PyObject *self, PyObject *args)
+{
+  return typy_make_iter (self, iter_items);
+}
+
+/* iterkeys() method.  */
+
+static PyObject *
+typy_iterkeys (PyObject *self, PyObject *args)
+{
+  return typy_make_iter (self, iter_keys);
+}
+
+/* Iterating over the class, same as iterkeys except for the function
+   signature.  */
+
+static PyObject *
+typy_iter (PyObject *self)
+{
+  return typy_make_iter (self, iter_keys);
+}
+
+/* itervalues() method.  */
+
+static PyObject *
+typy_itervalues (PyObject *self, PyObject *args)
+{
+  return typy_make_iter (self, iter_values);
+}
+
+/* Return a reference to the type iterator.  */
+
+static PyObject *
+typy_iterator_iter (PyObject *self)
+{
+  Py_INCREF (self);
+  return self;
+}
+
+/* Return the next field in the iteration through the list of fields
+   of the type.  */
+
+static PyObject *
+typy_iterator_iternext (PyObject *self)
+{
+  typy_iterator_object *iter_obj = (typy_iterator_object *) self;
+  struct type *type = iter_obj->source->type;
+  int i;
+  PyObject *result;
+  
+  if (iter_obj->field < TYPE_NFIELDS (type))
+    {
+      result = make_fielditem (type, iter_obj->field, iter_obj->kind);
+      if (result != NULL)
+	iter_obj->field++;
+      return result;
+    }
+
+  return NULL;
+}
+
+static void
+typy_iterator_dealloc (PyObject *obj)
+{
+  typy_iterator_object *iter_obj = (typy_iterator_object *) obj;
+
+  Py_DECREF (iter_obj->source);
+}
+
 /* Create a new Type referring to TYPE.  */
 PyObject *
 type_to_type_object (struct type *type)
@@ -1067,6 +1369,8 @@
     return;
   if (PyType_Ready (&field_object_type) < 0)
     return;
+  if (PyType_Ready (&type_iterator_object_type) < 0)
+    return;
 
   for (i = 0; pyty_codes[i].name; ++i)
     {
@@ -1080,6 +1384,10 @@
   Py_INCREF (&type_object_type);
   PyModule_AddObject (gdb_module, "Type", (PyObject *) &type_object_type);
 
+  Py_INCREF (&type_iterator_object_type);
+  PyModule_AddObject (gdb_module, "TypeIterator",
+		      (PyObject *) &type_iterator_object_type);
+
   Py_INCREF (&field_object_type);
   PyModule_AddObject (gdb_module, "Field", (PyObject *) &field_object_type);
 }
@@ -1102,13 +1410,35 @@
   { "array", typy_array, METH_VARARGS,
     "array (N) -> Type\n\
 Return a type which represents an array of N objects of this type." },
+   { "__contains__", typy_has_key, METH_VARARGS,
+     "T.__contains__(k) -> True if T has a field named k, else False" },
   { "const", typy_const, METH_NOARGS,
     "const () -> Type\n\
 Return a const variant of this type." },
   { "fields", typy_fields, METH_NOARGS,
-    "field () -> list\n\
-Return a sequence holding all the fields of this type.\n\
-Each field is a dictionary." },
+    "fields () -> list\n\
+Return a list holding all the fields of this type.\n\
+Each field is a gdb.Field object." },
+  { "get", typy_get, METH_VARARGS,
+    "T.get(k[,default]) -> returns field named k in T, if it exists;\n\
+otherwise returns default, if supplied, or None if not." },
+  { "has_key", typy_has_key, METH_VARARGS,
+    "T.has_key(k) -> True if T has a field named k, else False" },
+  { "items", typy_items, METH_NOARGS,
+    "items () -> list\n\
+Return a list of (name, field) pairs of this type.\n\
+Each field is a gdb.Field object." },
+  { "iteritems", typy_iteritems, METH_NOARGS,
+    "iteritems () -> an iterator over the (name, field)\n\
+pairs of this type.  Each field is a gdb.Field object." },
+  { "iterkeys", typy_iterkeys, METH_NOARGS,
+    "iterkeys () -> an iterator over the field names of this type." },
+  { "itervalues", typy_itervalues, METH_NOARGS,
+    "itervalues () -> an iterator over the fields of this type.\n\
+Each field is a gdb.Field object." },
+  { "keys", typy_field_names, METH_NOARGS,
+    "keys () -> list\n\
+Return a list holding all the fields names of this type." },
   { "pointer", typy_pointer, METH_NOARGS,
     "pointer () -> Type\n\
 Return a type of pointer to this type." },
@@ -1130,12 +1460,22 @@
   { "unqualified", typy_unqualified, METH_NOARGS,
     "unqualified () -> Type\n\
 Return a variant of this type without const or volatile attributes." },
+  { "values", typy_fields, METH_NOARGS,
+    "values () -> list\n\
+Return a list holding all the fields of this type.\n\
+Each field is a gdb.Field object." },
   { "volatile", typy_volatile, METH_NOARGS,
     "volatile () -> Type\n\
 Return a volatile variant of this type" },
   { NULL }
 };
 
+static PyMappingMethods typy_mapping = {
+  typy_length,
+  typy_getitem,
+  NULL				  /* no "set" method */
+};
+
 static PyTypeObject type_object_type =
 {
   PyObject_HEAD_INIT (NULL)
@@ -1151,7 +1491,7 @@
   0,				  /*tp_repr*/
   0,				  /*tp_as_number*/
   0,				  /*tp_as_sequence*/
-  0,				  /*tp_as_mapping*/
+  &typy_mapping,		  /*tp_as_mapping*/
   0,				  /*tp_hash */
   0,				  /*tp_call*/
   typy_str,			  /*tp_str*/
@@ -1164,7 +1504,7 @@
   0,				  /* tp_clear */
   typy_richcompare,		  /* tp_richcompare */
   0,				  /* tp_weaklistoffset */
-  0,				  /* tp_iter */
+  typy_iter,			  /* tp_iter */
   0,				  /* tp_iternext */
   type_object_methods,		  /* tp_methods */
   0,				  /* tp_members */
@@ -1221,3 +1561,35 @@
   0,				  /* tp_alloc */
   0,				  /* tp_new */
 };
+
+static PyTypeObject type_iterator_object_type = {
+  PyObject_HEAD_INIT (NULL)
+  0,				  /*ob_size*/
+  "gdb.TypeIterator",		  /*tp_name*/
+  sizeof (typy_iterator_object),  /*tp_basicsize*/
+  0,				  /*tp_itemsize*/
+  typy_iterator_dealloc,	  /*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_HAVE_ITER,  /*tp_flags*/
+  "GDB type iterator object",	  /*tp_doc */
+  0,				  /*tp_traverse */
+  0,				  /*tp_clear */
+  0,				  /*tp_richcompare */
+  0,				  /*tp_weaklistoffset */
+  typy_iterator_iter,             /*tp_iter */
+  typy_iterator_iternext,	  /*tp_iternext */
+  0				  /*tp_methods */
+};
Index: python/python-internal.h
===================================================================
RCS file: /cvs/src/src/gdb/python/python-internal.h,v
retrieving revision 1.48
diff -u -r1.48 python-internal.h
--- python/python-internal.h	15 Sep 2011 12:42:30 -0000	1.48
+++ python/python-internal.h	26 Sep 2011 15:54:34 -0000
@@ -104,6 +104,8 @@
 
 #include "exceptions.h"
 
+enum gdbpy_iter_kind { iter_keys, iter_values, iter_items };
+
 struct block;
 struct value;
 struct language_defn;
Index: testsuite/gdb.python/py-type.c
===================================================================
RCS file: /cvs/src/src/gdb/testsuite/gdb.python/py-type.c,v
retrieving revision 1.4
diff -u -r1.4 py-type.c
--- testsuite/gdb.python/py-type.c	1 Jan 2011 15:33:49 -0000	1.4
+++ testsuite/gdb.python/py-type.c	26 Sep 2011 15:54:34 -0000
@@ -43,6 +43,10 @@
 
 #endif
 
+enum E
+{ v1, v2, v3
+};
+
 int
 main ()
 {
@@ -56,9 +60,12 @@
   d.e = 3;
   d.f = 4;
 #endif
-
+  enum E e;
+  
   st.a = 3;
   st.b = 5;
 
+  e = v2;
+  
   return 0;      /* break to inspect struct and array.  */
 }
Index: testsuite/gdb.python/py-type.exp
===================================================================
RCS file: /cvs/src/src/gdb/testsuite/gdb.python/py-type.exp,v
retrieving revision 1.13
diff -u -r1.13 py-type.exp
--- testsuite/gdb.python/py-type.exp	26 Jul 2011 18:38:55 -0000	1.13
+++ testsuite/gdb.python/py-type.exp	26 Sep 2011 15:54:34 -0000
@@ -86,6 +86,14 @@
   gdb_test "python print fields\[0\].name" "a" "Check structure field a name"
   gdb_test "python print fields\[1\].name" "b" "Check structure field b name"
 
+  # Test Python mapping behavior of gdb.Type for structs/classes
+  gdb_test "python print len(st.type)" "2" "Check number of fields"
+  gdb_test "python print st.type\['a'\].name" "a" "Check fields lookup by name"
+    gdb_test "python print \[v.bitpos for v in st.type.itervalues()\]" {\[0L, 32L\]} "Check fields iteration over values"
+    gdb_test "python print \[(n, v.bitpos) for (n, v) in st.type.items()\]" {\[\('a', 0L\), \('b', 32L\)\]} "Check fields items list"
+  gdb_test "python print 'a' in st.type" "True" "Check field name exists test"
+  gdb_test "python print 'nosuch' in st.type" "False" "Check field name nonexists test"
+  
   # Test regression PR python/10805
   gdb_py_test_silent_cmd "print ar" "print value" 1
   gdb_py_test_silent_cmd "python ar = gdb.history (0)" "get value from  history" 1
@@ -101,6 +109,21 @@
   gdb_test "python print ar\[0\].type == ar\[0\].type" "True"
 }
 
+proc test_enums {} {
+  gdb_py_test_silent_cmd "print e" "print value" 1
+  gdb_py_test_silent_cmd "python e = gdb.history (0)" "get value from  history" 1
+  gdb_py_test_silent_cmd "python fields = e.type.fields()" "get value from history" 1
+  gdb_test "python print len(fields)" "3" "Check the number of enum fields"
+  gdb_test "python print fields\[0\].name" "v1" "Check enum field name"
+  gdb_test "python print fields\[1\].name" "v2" "Check enum field name"
+
+  # Ditto but by mapping operations
+  gdb_test "python print len(e.type)" "3" "Check the number of enum fields"
+  gdb_test "python print e.type\['v1'\].name" "v1" "Check enum field lookup by name"
+  gdb_test "python print e.type\['v3'\].name" "v3" "Check enum field lookup by name"
+    gdb_test "python print \[v.bitpos for v in e.type.itervalues()\]" {\[0L, 1L, 2L\]} "Check num fields iteration over values"
+    gdb_test "python print \[(n, v.bitpos) for (n, v) in e.type.items()\]" {\[\('v1', 0L\), \('v2', 1L\), \('v3', 2L\)\]} "Check enum fields items list"
+}
 proc test_base_class {} {
   gdb_py_test_silent_cmd "print d" "print value" 1
   gdb_py_test_silent_cmd "python d = gdb.history (0)" "get value from  history" 1
@@ -169,6 +192,7 @@
 
 runto_bp "break to inspect struct and array."
 test_fields "c"
+test_enums
 
 # Perform C++ Tests.
 build_inferior "${binfile}-cxx" "c++"
@@ -178,3 +202,4 @@
 test_base_class
 test_range
 test_template
+test_enums


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

* Re: [RFA] Re: Python: add field access by name and standard python mapping methods to gdb.Type
  2011-09-26 17:15       ` [RFA] " Paul Koning
@ 2011-09-28 19:25         ` Doug Evans
  2011-09-28 20:41           ` Paul Koning
  0 siblings, 1 reply; 26+ messages in thread
From: Doug Evans @ 2011-09-28 19:25 UTC (permalink / raw)
  To: Paul Koning; +Cc: gdb-patches

On Mon, Sep 26, 2011 at 9:00 AM, Paul Koning <paulkoning@comcast.net> wrote:
>
> On Sep 23, 2011, at 12:33 PM, Doug Evans wrote:
>
> > On Fri, Sep 16, 2011 at 8:27 AM, Paul Koning <paulkoning@comcast.net> wrote:
> >>
> >> Below is an update with the various comments addressed.
> >
> > Hi.  This email is for just a couple of formatting nits.
> >
> >> 2011-09-15  Paul Koning  <paul_koning@dell.com>
> >>
> >>        * python/py-type.c (make_fielditem, typy_field_names, typy_items,
> >>        typy_length, typy_get, typy_has_key, typy_make_iter,
> >>        typy_iterkeys, typy_iteritems, typy_itervalues, typy_iter,
> >>        typy_iterator_iter, typy_iterator_iternext,
> >>        typy_iterator_dealloc): : New functions to implement standard
> >>        Python mapping methods on gdb.Type object.
> >>        (gdb.TypeIterator): New Python type.
> >>        * python/python-internal.h (gdbpy_iter_kind): New enum.
> >>        * doc/gdb.texinfo (gdb.Type): Document field access by dictionary
> >>        key syntax.
> >
> > Technically speaking, the rule is you can't extend a parenthesized name list
> > over multiple lines.  At least that's the rule I've been asked to follow.
> > I'd like to see some flexibility here, and I'm not asking you change this here.
> > Just pointing it out.
> >
> >> +/* Helper function for Type standard mapping methods.  Returns a
> >> +   Python object for field i of the type.  "kind" specifies what to
> >> +   return: the name of the field, a gdb.Field object corresponding to
> >> +   the field, or a tuple consisting of field name and gdb.Field
> >> +   object.  */
> >> +static PyObject *
> >> +make_fielditem (struct type *type, int i, enum gdbpy_iter_kind kind)
> >
> > For functions the coding standards say to have a blank line between
> > its comment and definition.
> > I realize parts of gdb don't follow this rule, but this rule I like :-),
> > it is documented, and I'd like to not make things worse.
> >
> > This needs to be fixed in several places.
>
> I've fixed both -- the ChangeLog is now formatted according to an earlier example I see in ChangeLog.
>
> Ok to commit?
>
>        paul
>
> 2011-09-26  Paul Koning  <paul_koning@dell.com>
>
>        * python/py-type.c (make_fielditem, typy_field_names, typy_items)
>        (typy_length, typy_get, typy_has_key, typy_make_iter)
>        (typy_iterkeys, typy_iteritems, typy_itervalues, typy_iter)
>        (typy_iterator_iter, typy_iterator_iternext)
>        (typy_iterator_dealloc): : New functions to implement standard
>        Python mapping methods on gdb.Type object.
>        (gdb.TypeIterator): New Python type.
>        * python/python-internal.h (gdbpy_iter_kind): New enum.
>        * doc/gdb.texinfo (gdb.Type): Document field access by dictionary
>        key syntax.
>
> 2011-09-26  Paul Koning  <paul_koning@dell.com>
>
>        * gdb.python/py-type.c (enum E): New.
>        * gdb.python/py-type.exp (test_fields): Add tests for Python
>        mapping access to fields.
>        (test_enums): New test for field access on enums.

Blech, sorry for the dup.

One changelog entry has two colons (::), but other than that
it's ok by me.  Thanks!


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

* Re: [RFA] Re: Python: add field access by name and standard python mapping methods to gdb.Type
  2011-09-28 19:25         ` Doug Evans
@ 2011-09-28 20:41           ` Paul Koning
  2011-10-04 15:14             ` Tom Tromey
  0 siblings, 1 reply; 26+ messages in thread
From: Paul Koning @ 2011-09-28 20:41 UTC (permalink / raw)
  To: Doug Evans; +Cc: gdb-patches


On Sep 28, 2011, at 1:49 PM, Doug Evans wrote:

> ...
> One changelog entry has two colons (::), but other than that
> it's ok by me.  Thanks!

Thanks.  Committed with these changelogs, same diff as previously mailed:

2011-09-28  Paul Koning  <paul_koning@dell.com>

	* python/py-type.c (make_fielditem, typy_field_names, typy_items)
	(typy_length, typy_get, typy_has_key, typy_make_iter)
	(typy_iterkeys, typy_iteritems, typy_itervalues, typy_iter)
	(typy_iterator_iter, typy_iterator_iternext)
	(typy_iterator_dealloc): New functions to implement standard
	Python mapping methods on gdb.Type object.
	(gdb.TypeIterator): New Python type.
	* python/python-internal.h (gdbpy_iter_kind): New enum.
	* doc/gdb.texinfo (gdb.Type): Document field access by dictionary
	key syntax.

2011-09-28  Paul Koning  <paul_koning@dell.com>

	* gdb.python/py-type.c (enum E): New.
	* gdb.python/py-type.exp (test_fields): Add tests for Python
	mapping access to fields.
	(test_enums): New test for field access on enums.

  paul


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

* Re: [RFA] Re: Python: add field access by name and standard python mapping methods to gdb.Type
  2011-09-28 20:41           ` Paul Koning
@ 2011-10-04 15:14             ` Tom Tromey
  2011-10-04 15:30               ` Paul Koning
  0 siblings, 1 reply; 26+ messages in thread
From: Tom Tromey @ 2011-10-04 15:14 UTC (permalink / raw)
  To: Paul Koning; +Cc: Doug Evans, gdb-patches

>>>>> "Paul" == Paul Koning <paulkoning@comcast.net> writes:

Paul> Thanks.  Committed with these changelogs, same diff as previously mailed:

Paul> 	* doc/gdb.texinfo (gdb.Type): Document field access by dictionary
Paul> 	key syntax.

For future reference, there is a separate ChangeLog in doc.  Entries for
documentation have to go there.

Paul> 2011-09-28  Paul Koning  <paul_koning@dell.com>
Paul> 	* gdb.python/py-type.c (enum E): New.
Paul> 	* gdb.python/py-type.exp (test_fields): Add tests for Python
Paul> 	mapping access to fields.
Paul> 	(test_enums): New test for field access on enums.

Could you write a NEWS entry for this change?

thanks,
Tom


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

* Re: [RFA] Re: Python: add field access by name and standard python mapping methods to gdb.Type
  2011-10-04 15:14             ` Tom Tromey
@ 2011-10-04 15:30               ` Paul Koning
  2011-10-04 15:42                 ` Eli Zaretskii
  0 siblings, 1 reply; 26+ messages in thread
From: Paul Koning @ 2011-10-04 15:30 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Doug Evans, gdb-patches


On Oct 4, 2011, at 11:13 AM, Tom Tromey wrote:

>>>>>> "Paul" == Paul Koning <paulkoning@comcast.net> writes:
> 
> Paul> Thanks.  Committed with these changelogs, same diff as previously mailed:
> 
> Paul> 	* doc/gdb.texinfo (gdb.Type): Document field access by dictionary
> Paul> 	key syntax.
> 
> For future reference, there is a separate ChangeLog in doc.  Entries for
> documentation have to go there.

I overlooked that file.  Thanks for the reference.  Should I move the entry there?

> 
> Paul> 2011-09-28  Paul Koning  <paul_koning@dell.com>
> Paul> 	* gdb.python/py-type.c (enum E): New.
> Paul> 	* gdb.python/py-type.exp (test_fields): Add tests for Python
> Paul> 	mapping access to fields.
> Paul> 	(test_enums): New test for field access on enums.
> 
> Could you write a NEWS entry for this change?

How about this?

	paul

2011-10-04  Paul Koning  <paul_koning@dell.com>

	* NEWS: Add entry for Python gdb.Type mapping methods.

Index: NEWS
===================================================================
RCS file: /cvs/src/src/gdb/NEWS,v
retrieving revision 1.453
diff -u -r1.453 NEWS
--- NEWS	15 Sep 2011 12:27:18 -0000	1.453
+++ NEWS	4 Oct 2011 15:28:44 -0000
@@ -41,6 +41,11 @@
   ** The "gdb.breakpoint" function has been deprecated in favor of
      "gdb.breakpoints".
 
+  ** Type objects for struct and union types now allow access to
+     the fields using standard Python dictionary (mapping) methods.
+     For example, "some_type['myfield']" now works, as does
+     "some_type.items()".
+
 * libthread-db-search-path now supports two special values: $sdir and $pdir.
   $sdir specifies the default system locations of shared libraries.
   $pdir specifies the directory where the libpthread used by the application


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

* Re: [RFA] Re: Python: add field access by name and standard python mapping methods to gdb.Type
  2011-10-04 15:30               ` Paul Koning
@ 2011-10-04 15:42                 ` Eli Zaretskii
  2011-10-04 15:56                   ` Paul Koning
  0 siblings, 1 reply; 26+ messages in thread
From: Eli Zaretskii @ 2011-10-04 15:42 UTC (permalink / raw)
  To: Paul Koning; +Cc: tromey, dje, gdb-patches

> From: Paul Koning <paulkoning@comcast.net>
> Date: Tue, 4 Oct 2011 11:29:58 -0400
> Cc: Doug Evans <dje@google.com>, gdb-patches@sourceware.org
> 
> 
> > For future reference, there is a separate ChangeLog in doc.  Entries for
> > documentation have to go there.
> 
> I overlooked that file.  Thanks for the reference.  Should I move the entry there?

Yes, please.

> > Could you write a NEWS entry for this change?
> 
> How about this?

Fine with me, thanks.


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

* Re: [RFA] Re: Python: add field access by name and standard python mapping methods to gdb.Type
  2011-10-04 15:42                 ` Eli Zaretskii
@ 2011-10-04 15:56                   ` Paul Koning
  2011-11-04 17:42                     ` Doug Evans
  0 siblings, 1 reply; 26+ messages in thread
From: Paul Koning @ 2011-10-04 15:56 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: tromey, dje, gdb-patches


On Oct 4, 2011, at 11:41 AM, Eli Zaretskii wrote:

>> From: Paul Koning <paulkoning@comcast.net>
>> Date: Tue, 4 Oct 2011 11:29:58 -0400
>> Cc: Doug Evans <dje@google.com>, gdb-patches@sourceware.org
>> 
>> 
>>> For future reference, there is a separate ChangeLog in doc.  Entries for
>>> documentation have to go there.
>> 
>> I overlooked that file.  Thanks for the reference.  Should I move the entry there?
> 
> Yes, please.

Done.

> 
>>> Could you write a NEWS entry for this change?
>> 
>> How about this?
> 
> Fine with me, thanks.

Committed.

	paul


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

* Re: [RFA] Re: Python: add field access by name and standard python mapping methods to gdb.Type
  2011-10-04 15:56                   ` Paul Koning
@ 2011-11-04 17:42                     ` Doug Evans
  2011-11-04 20:41                       ` Paul Koning
  0 siblings, 1 reply; 26+ messages in thread
From: Doug Evans @ 2011-11-04 17:42 UTC (permalink / raw)
  To: Paul Koning; +Cc: Eli Zaretskii, tromey, gdb-patches

On Tue, Oct 4, 2011 at 8:56 AM, Paul Koning <paulkoning@comcast.net> wrote:
>
> On Oct 4, 2011, at 11:41 AM, Eli Zaretskii wrote:
>
>>> From: Paul Koning <paulkoning@comcast.net>
>>> Date: Tue, 4 Oct 2011 11:29:58 -0400
>>> Cc: Doug Evans <dje@google.com>, gdb-patches@sourceware.org
>>>
>>>
>>>> For future reference, there is a separate ChangeLog in doc.  Entries for
>>>> documentation have to go there.
>>>
>>> I overlooked that file.  Thanks for the reference.  Should I move the entry there?
>>
>> Yes, please.
>
> Done.
>
>>
>>>> Could you write a NEWS entry for this change?
>>>
>>> How about this?
>>
>> Fine with me, thanks.
>
> Committed.

Ummm, hi.
I know I looked at the patch and approved it myself, but having played
with it for awhile I'm having second thoughts.
And before a release goes out I'd like to get this resolved.
If you want I'll do the work, or at least help however I can.

One way to look at my reasoning is that a type "has a" field list but
it's not the case that a type "is a" field list.
And I'm uncomfortable with len(gdb.parse_and_eval("1").type) == 0.
IOW, len(gdb.Type of "int") is now 0.  I think it should flag an exception.

OTOH, adding the new support to the result of gdb.Type.fields() is great.

Anyone object to me changing things and moving the new iterator
support to gdb.Type.fields()?
Or do people disagree with my reasoning?
I haven't looked into what's involved.  At this point I just want to
get the user-visible semantics right.


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

* Re: [RFA] Re: Python: add field access by name and standard python mapping methods to gdb.Type
  2011-11-04 17:42                     ` Doug Evans
@ 2011-11-04 20:41                       ` Paul Koning
  2011-11-04 23:05                         ` Doug Evans
  0 siblings, 1 reply; 26+ messages in thread
From: Paul Koning @ 2011-11-04 20:41 UTC (permalink / raw)
  To: Doug Evans; +Cc: Eli Zaretskii, tromey, gdb-patches


On Nov 4, 2011, at 1:41 PM, Doug Evans wrote:

> On Tue, Oct 4, 2011 at 8:56 AM, Paul Koning <paulkoning@comcast.net> wrote:
>> 
>> On Oct 4, 2011, at 11:41 AM, Eli Zaretskii wrote:
>> 
>>>> From: Paul Koning <paulkoning@comcast.net>
>>>> Date: Tue, 4 Oct 2011 11:29:58 -0400
>>>> Cc: Doug Evans <dje@google.com>, gdb-patches@sourceware.org
>>>> 
>>>> 
>>>>> For future reference, there is a separate ChangeLog in doc.  Entries for
>>>>> documentation have to go there.
>>>> 
>>>> I overlooked that file.  Thanks for the reference.  Should I move the entry there?
>>> 
>>> Yes, please.
>> 
>> Done.
>> 
>>> 
>>>>> Could you write a NEWS entry for this change?
>>>> 
>>>> How about this?
>>> 
>>> Fine with me, thanks.
>> 
>> Committed.
> 
> Ummm, hi.
> I know I looked at the patch and approved it myself, but having played
> with it for awhile I'm having second thoughts.
> And before a release goes out I'd like to get this resolved.
> If you want I'll do the work, or at least help however I can.
> 
> One way to look at my reasoning is that a type "has a" field list but
> it's not the case that a type "is a" field list.
> And I'm uncomfortable with len(gdb.parse_and_eval("1").type) == 0.
> IOW, len(gdb.Type of "int") is now 0.  I think it should flag an exception.
> 
> OTOH, adding the new support to the result of gdb.Type.fields() is great.
> 
> Anyone object to me changing things and moving the new iterator
> support to gdb.Type.fields()?
> Or do people disagree with my reasoning?
> I haven't looked into what's involved.  At this point I just want to
> get the user-visible semantics right.

Part of my reasoning is to have gdb.Value and gdb.Type look alike.  gdb.Value always had field lookup by name, i.e., it behaves like a Python dictionary.  So I wanted to make the same apply to gdb.Type since the analogy seemed obvious.  And in both cases, I wanted the normal Python dict methods to be available.  (For gdb.Value, that's not submitted yet.)

In my view, gdb.Type.fields remains as a backward compatibility synonym for gdb.Type.values (the standard dict method).

I do agree that having len() return 0 instead of an error seems wrong.

	paul


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

* Re: [RFA] Re: Python: add field access by name and standard python mapping methods to gdb.Type
  2011-11-04 20:41                       ` Paul Koning
@ 2011-11-04 23:05                         ` Doug Evans
  2011-11-05 14:36                           ` Paul Koning
  0 siblings, 1 reply; 26+ messages in thread
From: Doug Evans @ 2011-11-04 23:05 UTC (permalink / raw)
  To: Paul Koning; +Cc: Eli Zaretskii, tromey, gdb-patches

On Fri, Nov 4, 2011 at 1:40 PM, Paul Koning <paulkoning@comcast.net> wrote:
>
> On Nov 4, 2011, at 1:41 PM, Doug Evans wrote:
>
>> On Tue, Oct 4, 2011 at 8:56 AM, Paul Koning <paulkoning@comcast.net> wrote:
>>>
>>> On Oct 4, 2011, at 11:41 AM, Eli Zaretskii wrote:
>>>
>>>>> From: Paul Koning <paulkoning@comcast.net>
>>>>> Date: Tue, 4 Oct 2011 11:29:58 -0400
>>>>> Cc: Doug Evans <dje@google.com>, gdb-patches@sourceware.org
>>>>>
>>>>>
>>>>>> For future reference, there is a separate ChangeLog in doc.  Entries for
>>>>>> documentation have to go there.
>>>>>
>>>>> I overlooked that file.  Thanks for the reference.  Should I move the entry there?
>>>>
>>>> Yes, please.
>>>
>>> Done.
>>>
>>>>
>>>>>> Could you write a NEWS entry for this change?
>>>>>
>>>>> How about this?
>>>>
>>>> Fine with me, thanks.
>>>
>>> Committed.
>>
>> Ummm, hi.
>> I know I looked at the patch and approved it myself, but having played
>> with it for awhile I'm having second thoughts.
>> And before a release goes out I'd like to get this resolved.
>> If you want I'll do the work, or at least help however I can.
>>
>> One way to look at my reasoning is that a type "has a" field list but
>> it's not the case that a type "is a" field list.
>> And I'm uncomfortable with len(gdb.parse_and_eval("1").type) == 0.
>> IOW, len(gdb.Type of "int") is now 0.  I think it should flag an exception.
>>
>> OTOH, adding the new support to the result of gdb.Type.fields() is great.
>>
>> Anyone object to me changing things and moving the new iterator
>> support to gdb.Type.fields()?
>> Or do people disagree with my reasoning?
>> I haven't looked into what's involved.  At this point I just want to
>> get the user-visible semantics right.
>
> Part of my reasoning is to have gdb.Value and gdb.Type look alike.  gdb.Value always had field lookup by name, i.e., it behaves like a Python dictionary.

For structs and such sure, but not so for scalars.
Scalars shouldn't behave like a dictionary.

If we're going for consistency between gdb.Value and gdb.Type then for
structs and such we'll need len(gdb.Value) to return the number of
fields. [Right?]

Maybe I'd be happy if gdb.Type (and maybe gdb.Value) were simply more
rigorous in throwing exceptions for invalid cases.

> So I wanted to make the same apply to gdb.Type since the analogy seemed obvious.  And in both cases, I wanted the normal Python dict methods to be available.  (For gdb.Value, that's not submitted yet.)
>
> In my view, gdb.Type.fields remains as a backward compatibility synonym for gdb.Type.values (the standard dict method).
>
> I do agree that having len() return 0 instead of an error seems wrong.

That could be fixed by having typy_length throw an appropriate error
for scalars, etc.


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

* Re: [RFA] Re: Python: add field access by name and standard python mapping methods to gdb.Type
  2011-11-04 23:05                         ` Doug Evans
@ 2011-11-05 14:36                           ` Paul Koning
  2011-11-05 21:04                             ` Doug Evans
  0 siblings, 1 reply; 26+ messages in thread
From: Paul Koning @ 2011-11-05 14:36 UTC (permalink / raw)
  To: Doug Evans; +Cc: Eli Zaretskii, tromey, gdb-patches


On Nov 4, 2011, at 7:04 PM, Doug Evans wrote:

> On Fri, Nov 4, 2011 at 1:40 PM, Paul Koning <paulkoning@comcast.net> wrote:
>> 
>> On Nov 4, 2011, at 1:41 PM, Doug Evans wrote:
>> 
>>> On Tue, Oct 4, 2011 at 8:56 AM, Paul Koning <paulkoning@comcast.net> wrote:
>>>> 
>>>> On Oct 4, 2011, at 11:41 AM, Eli Zaretskii wrote:
>>>> 
>>>>>> From: Paul Koning <paulkoning@comcast.net>
>>>>>> Date: Tue, 4 Oct 2011 11:29:58 -0400
>>>>>> Cc: Doug Evans <dje@google.com>, gdb-patches@sourceware.org
>>>>>> 
>>>>>> 
>>>>>>> For future reference, there is a separate ChangeLog in doc.  Entries for
>>>>>>> documentation have to go there.
>>>>>> 
>>>>>> I overlooked that file.  Thanks for the reference.  Should I move the entry there?
>>>>> 
>>>>> Yes, please.
>>>> 
>>>> Done.
>>>> 
>>>>> 
>>>>>>> Could you write a NEWS entry for this change?
>>>>>> 
>>>>>> How about this?
>>>>> 
>>>>> Fine with me, thanks.
>>>> 
>>>> Committed.
>>> 
>>> Ummm, hi.
>>> I know I looked at the patch and approved it myself, but having played
>>> with it for awhile I'm having second thoughts.
>>> And before a release goes out I'd like to get this resolved.
>>> If you want I'll do the work, or at least help however I can.
>>> 
>>> One way to look at my reasoning is that a type "has a" field list but
>>> it's not the case that a type "is a" field list.
>>> And I'm uncomfortable with len(gdb.parse_and_eval("1").type) == 0.
>>> IOW, len(gdb.Type of "int") is now 0.  I think it should flag an exception.
>>> 
>>> OTOH, adding the new support to the result of gdb.Type.fields() is great.
>>> 
>>> Anyone object to me changing things and moving the new iterator
>>> support to gdb.Type.fields()?
>>> Or do people disagree with my reasoning?
>>> I haven't looked into what's involved.  At this point I just want to
>>> get the user-visible semantics right.
>> 
>> Part of my reasoning is to have gdb.Value and gdb.Type look alike.  gdb.Value always had field lookup by name, i.e., it behaves like a Python dictionary.
> 
> For structs and such sure, but not so for scalars.
> Scalars shouldn't behave like a dictionary.

Quite right.

> 
> If we're going for consistency between gdb.Value and gdb.Type then for
> structs and such we'll need len(gdb.Value) to return the number of
> fields. [Right?]

Yes, and that's part of what I intend to propose.
> 
> Maybe I'd be happy if gdb.Type (and maybe gdb.Value) were simply more
> rigorous in throwing exceptions for invalid cases.

I agree.  I'll put that together.

	paul


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

* Re: [RFA] Re: Python: add field access by name and standard python mapping methods to gdb.Type
  2011-11-05 14:36                           ` Paul Koning
@ 2011-11-05 21:04                             ` Doug Evans
  2011-11-08 21:40                               ` [RFA] Python: raise exception on field-related gdb.Type methods if it's not struct or union Paul Koning
  0 siblings, 1 reply; 26+ messages in thread
From: Doug Evans @ 2011-11-05 21:04 UTC (permalink / raw)
  To: Paul Koning; +Cc: Eli Zaretskii, tromey, gdb-patches

On Sat, Nov 5, 2011 at 7:36 AM, Paul Koning <paulkoning@comcast.net> wrote:
>> Maybe I'd be happy if gdb.Type (and maybe gdb.Value) were simply more
>> rigorous in throwing exceptions for invalid cases.
>
> I agree.  I'll put that together.

Sounds great if it's possible.

I don't know enough about the C Python API, but when I tried a simple
hack to have len(scalar_type) throw an exception "not scalar_type"
started throwing exceptions too.  :-(  See PyObject_IsTrue.


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

* [RFA] Python: raise exception on field-related gdb.Type methods if it's not struct or union
  2011-11-05 21:04                             ` Doug Evans
@ 2011-11-08 21:40                               ` Paul Koning
  2011-11-09  0:29                                 ` Doug Evans
  0 siblings, 1 reply; 26+ messages in thread
From: Paul Koning @ 2011-11-08 21:40 UTC (permalink / raw)
  To: Doug Evans; +Cc: Eli Zaretskii, tromey, gdb-patches


On Nov 5, 2011, at 5:04 PM, Doug Evans wrote:

> On Sat, Nov 5, 2011 at 7:36 AM, Paul Koning <paulkoning@comcast.net> wrote:
>>> Maybe I'd be happy if gdb.Type (and maybe gdb.Value) were simply more
>>> rigorous in throwing exceptions for invalid cases.
>> 
>> I agree.  I'll put that together.
> 
> Sounds great if it's possible.
> 
> I don't know enough about the C Python API, but when I tried a simple
> hack to have len(scalar_type) throw an exception "not scalar_type"
> started throwing exceptions too.  :-(  See PyObject_IsTrue.

I found the answer to that question with a bit of searching.   Attached is a patch that raises an exception (TypeError) for len() and all the field reference methods if the type isn't struct or union.

Does this need new testcases?

	paul

ChangeLog:

2011-11-08  Paul Koning  <paul_koning@dell.com>

	* python/py-type.c (typy_deref): New function.
	(typy_nonzero): New function.
	(typy_length): Raise exception if not struct or union type.
	(typy_getitem): Ditto.
	(typy_has_key): Ditto.
	(typy_make_iter): Ditto.

Index: python/py-type.c
===================================================================
RCS file: /cvs/src/src/gdb/python/py-type.c,v
retrieving revision 1.28
diff -u -p -r1.28 py-type.c
--- python/py-type.c	4 Nov 2011 11:57:04 -0000	1.28
+++ python/py-type.c	8 Nov 2011 21:36:40 -0000
@@ -350,6 +350,45 @@ typy_strip_typedefs (PyObject *self, PyO
   return type_to_type_object (check_typedef (type));
 }
 
+/* Strip typedefs and pointers/reference from a type.  Then check that
+   it is a struct or union type.  If not, raise TypeError.  */
+static struct type *
+typy_deref (struct type *type)
+{
+  volatile struct gdb_exception except;
+
+  for (;;)
+    {
+      TRY_CATCH (except, RETURN_MASK_ALL)
+		{
+	  CHECK_TYPEDEF (type);
+	}
+      /* Don't use GDB_PY_HANDLE_EXCEPTION here because that returns
+	 a (NULL) pointer of the wrong type.  */
+      if (except.reason < 0)
+	{
+	  gdbpy_convert_exception (except);
+	  return NULL;
+	}
+
+      if (TYPE_CODE (type) != TYPE_CODE_PTR
+	  && TYPE_CODE (type) != TYPE_CODE_REF)
+	break;
+      type = TYPE_TARGET_TYPE (type);
+    }
+
+  /* If this is not a struct or union type, raise TypeError exception.  */
+  if (TYPE_CODE (type) != TYPE_CODE_STRUCT 
+      && TYPE_CODE (type) != TYPE_CODE_UNION)
+    {
+      PyErr_SetString (PyExc_TypeError,
+		       "Type is not a structure or union type.");
+      return NULL;
+    }
+  
+  return type;
+}
+
 /* Return an array type.  */
 
 static PyObject *
@@ -1124,9 +1163,22 @@ typy_length (PyObject *self)
 {
   struct type *type = ((type_object *) self)->type;
 
+  type = typy_deref (type);
+  if (type == NULL)
+    return -1;
+
   return TYPE_NFIELDS (type);
 }
 
+/* Implements boolean evaluation of gdb.Type.  Handle this like other
+   Python objects that don't have a meaningful truth value -- all 
+   values are true.  */
+static int
+typy_nonzero (PyObject *self)
+{
+  return 1;
+}
+
 /* Return a gdb.Field object for the field named by the argument.  */
 
 static PyObject *
@@ -1145,20 +1197,10 @@ typy_getitem (PyObject *self, PyObject *
      using lookup_struct_elt_type, portions of that function are
      copied here.  */
 
-  for (;;)
-    {
-      TRY_CATCH (except, RETURN_MASK_ALL)
-	{
-	  CHECK_TYPEDEF (type);
-	}
-      GDB_PY_HANDLE_EXCEPTION (except);
-
-      if (TYPE_CODE (type) != TYPE_CODE_PTR
-	  && TYPE_CODE (type) != TYPE_CODE_REF)
-	break;
-      type = TYPE_TARGET_TYPE (type);
-    }
-
+  type = typy_deref (type);
+  if (type == NULL)
+    return NULL;
+  
   for (i = 0; i < TYPE_NFIELDS (type); i++)
     {
       char *t_field_name = TYPE_FIELD_NAME (type, i);
@@ -1216,18 +1258,9 @@ typy_has_key (PyObject *self, PyObject *
      using lookup_struct_elt_type, portions of that function are
      copied here.  */
 
-  for (;;)
-    {
-      TRY_CATCH (except, RETURN_MASK_ALL)
-	{
-	  CHECK_TYPEDEF (type);
-	}
-      GDB_PY_HANDLE_EXCEPTION (except);
-      if (TYPE_CODE (type) != TYPE_CODE_PTR
-	  && TYPE_CODE (type) != TYPE_CODE_REF)
-	break;
-      type = TYPE_TARGET_TYPE (type);
-    }
+  type = typy_deref (type);
+  if (type == NULL)
+    return NULL;
 
   for (i = 0; i < TYPE_NFIELDS (type); i++)
     {
@@ -1246,6 +1279,10 @@ typy_make_iter (PyObject *self, enum gdb
 {
   typy_iterator_object *typy_iter_obj;
 
+  /* Check that "self" is a structure or union type.  */
+  if (typy_deref (((type_object *) self)->type) == NULL)
+    return NULL;
+  
   typy_iter_obj = PyObject_New (typy_iterator_object,
 				&type_iterator_object_type);
   if (typy_iter_obj == NULL)
@@ -1499,6 +1536,32 @@ Return a volatile variant of this type" 
   { NULL }
 };
 
+static PyNumberMethods type_object_as_number = {
+  NULL,			      /* nb_add */
+  NULL,			      /* nb_subtract */
+  NULL,			      /* nb_multiply */
+  NULL,			      /* nb_divide */
+  NULL,			      /* nb_remainder */
+  NULL,			      /* nb_divmod */
+  NULL,			      /* nb_power */
+  NULL,			      /* nb_negative */
+  NULL,			      /* nb_positive */
+  NULL,			      /* nb_absolute */
+  typy_nonzero,		      /* nb_nonzero */
+  NULL,			      /* nb_invert */
+  NULL,			      /* nb_lshift */
+  NULL,			      /* nb_rshift */
+  NULL,			      /* nb_and */
+  NULL,			      /* nb_xor */
+  NULL,			      /* nb_or */
+  NULL,			      /* nb_coerce */
+  NULL,			      /* nb_int */
+  NULL,			      /* nb_long */
+  NULL,			      /* nb_float */
+  NULL,			      /* nb_oct */
+  NULL			      /* nb_hex */
+};
+
 static PyMappingMethods typy_mapping = {
   typy_length,
   typy_getitem,
@@ -1518,7 +1581,7 @@ static PyTypeObject type_object_type =
   0,				  /*tp_setattr*/
   0,				  /*tp_compare*/
   0,				  /*tp_repr*/
-  0,				  /*tp_as_number*/
+  &type_object_as_number,	  /*tp_as_number*/
   0,				  /*tp_as_sequence*/
   &typy_mapping,		  /*tp_as_mapping*/
   0,				  /*tp_hash */


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

* Re: [RFA] Python: raise exception on field-related gdb.Type methods if it's not struct or union
  2011-11-08 21:40                               ` [RFA] Python: raise exception on field-related gdb.Type methods if it's not struct or union Paul Koning
@ 2011-11-09  0:29                                 ` Doug Evans
  2011-11-09  1:53                                   ` Paul Koning
  2011-11-09 18:10                                   ` Paul Koning
  0 siblings, 2 replies; 26+ messages in thread
From: Doug Evans @ 2011-11-09  0:29 UTC (permalink / raw)
  To: Paul Koning; +Cc: Eli Zaretskii, tromey, gdb-patches

On Tue, Nov 8, 2011 at 1:40 PM, Paul Koning <paulkoning@comcast.net> wrote:
>
> On Nov 5, 2011, at 5:04 PM, Doug Evans wrote:
>
>> On Sat, Nov 5, 2011 at 7:36 AM, Paul Koning <paulkoning@comcast.net> wrote:
>>>> Maybe I'd be happy if gdb.Type (and maybe gdb.Value) were simply more
>>>> rigorous in throwing exceptions for invalid cases.
>>>
>>> I agree.  I'll put that together.
>>
>> Sounds great if it's possible.
>>
>> I don't know enough about the C Python API, but when I tried a simple
>> hack to have len(scalar_type) throw an exception "not scalar_type"
>> started throwing exceptions too.  :-(  See PyObject_IsTrue.
>
> I found the answer to that question with a bit of searching.   Attached is a patch that raises an exception (TypeError) for len() and all the field reference methods if the type isn't struct or union.

I wasn't sure using as_number here was kosher.  "works for me" if it
does the job.

> Does this need new testcases?

Yeah, I think so.
Verify "not type" DTRT, and affected operations on scalar types flag
the right error.

One question I have is: for struct types that have an empty field list
(say an empty struct), what's the result of "not type".  True or
False?  I can argue either way, and I'm not sure what's the best
answer, long term.
[But then again, my python fu isn't enough to be comfortable with any
conclusion I reach.]

Thanks very much for doing this!

btw, a couple of nits:
1) Please put a blank line between a function's comment and its definition.
There are several occurrences of this.
2) Can you rename typy_deref?  typy_get_struct works for me, I'm sure
there's a better name.
    "deref" feels confusing, one can deref a pointer to anything, not
just structs/unions, and
    the function doesn't necessarily do a dereference.


>        paul
>
> ChangeLog:
>
> 2011-11-08  Paul Koning  <paul_koning@dell.com>
>
>        * python/py-type.c (typy_deref): New function.
>        (typy_nonzero): New function.
>        (typy_length): Raise exception if not struct or union type.
>        (typy_getitem): Ditto.
>        (typy_has_key): Ditto.
>        (typy_make_iter): Ditto.
>
> Index: python/py-type.c
> ===================================================================
> RCS file: /cvs/src/src/gdb/python/py-type.c,v
> retrieving revision 1.28
> diff -u -p -r1.28 py-type.c
> --- python/py-type.c    4 Nov 2011 11:57:04 -0000       1.28
> +++ python/py-type.c    8 Nov 2011 21:36:40 -0000
> @@ -350,6 +350,45 @@ typy_strip_typedefs (PyObject *self, PyO
>   return type_to_type_object (check_typedef (type));
>  }
>
> +/* Strip typedefs and pointers/reference from a type.  Then check that
> +   it is a struct or union type.  If not, raise TypeError.  */
> +static struct type *
> +typy_deref (struct type *type)
> +{
> +  volatile struct gdb_exception except;
> +
> +  for (;;)
> +    {
> +      TRY_CATCH (except, RETURN_MASK_ALL)
> +               {
> +         CHECK_TYPEDEF (type);
> +       }
> +      /* Don't use GDB_PY_HANDLE_EXCEPTION here because that returns
> +        a (NULL) pointer of the wrong type.  */
> +      if (except.reason < 0)
> +       {
> +         gdbpy_convert_exception (except);
> +         return NULL;
> +       }
> +
> +      if (TYPE_CODE (type) != TYPE_CODE_PTR
> +         && TYPE_CODE (type) != TYPE_CODE_REF)
> +       break;
> +      type = TYPE_TARGET_TYPE (type);
> +    }
> +
> +  /* If this is not a struct or union type, raise TypeError exception.  */
> +  if (TYPE_CODE (type) != TYPE_CODE_STRUCT
> +      && TYPE_CODE (type) != TYPE_CODE_UNION)
> +    {
> +      PyErr_SetString (PyExc_TypeError,
> +                      "Type is not a structure or union type.");
> +      return NULL;
> +    }
> +
> +  return type;
> +}
> +
>  /* Return an array type.  */
>
>  static PyObject *
> @@ -1124,9 +1163,22 @@ typy_length (PyObject *self)
>  {
>   struct type *type = ((type_object *) self)->type;
>
> +  type = typy_deref (type);
> +  if (type == NULL)
> +    return -1;
> +
>   return TYPE_NFIELDS (type);
>  }
>
> +/* Implements boolean evaluation of gdb.Type.  Handle this like other
> +   Python objects that don't have a meaningful truth value -- all
> +   values are true.  */
> +static int
> +typy_nonzero (PyObject *self)
> +{
> +  return 1;
> +}
> +
>  /* Return a gdb.Field object for the field named by the argument.  */
>
>  static PyObject *
> @@ -1145,20 +1197,10 @@ typy_getitem (PyObject *self, PyObject *
>      using lookup_struct_elt_type, portions of that function are
>      copied here.  */
>
> -  for (;;)
> -    {
> -      TRY_CATCH (except, RETURN_MASK_ALL)
> -       {
> -         CHECK_TYPEDEF (type);
> -       }
> -      GDB_PY_HANDLE_EXCEPTION (except);
> -
> -      if (TYPE_CODE (type) != TYPE_CODE_PTR
> -         && TYPE_CODE (type) != TYPE_CODE_REF)
> -       break;
> -      type = TYPE_TARGET_TYPE (type);
> -    }
> -
> +  type = typy_deref (type);
> +  if (type == NULL)
> +    return NULL;
> +
>   for (i = 0; i < TYPE_NFIELDS (type); i++)
>     {
>       char *t_field_name = TYPE_FIELD_NAME (type, i);
> @@ -1216,18 +1258,9 @@ typy_has_key (PyObject *self, PyObject *
>      using lookup_struct_elt_type, portions of that function are
>      copied here.  */
>
> -  for (;;)
> -    {
> -      TRY_CATCH (except, RETURN_MASK_ALL)
> -       {
> -         CHECK_TYPEDEF (type);
> -       }
> -      GDB_PY_HANDLE_EXCEPTION (except);
> -      if (TYPE_CODE (type) != TYPE_CODE_PTR
> -         && TYPE_CODE (type) != TYPE_CODE_REF)
> -       break;
> -      type = TYPE_TARGET_TYPE (type);
> -    }
> +  type = typy_deref (type);
> +  if (type == NULL)
> +    return NULL;
>
>   for (i = 0; i < TYPE_NFIELDS (type); i++)
>     {
> @@ -1246,6 +1279,10 @@ typy_make_iter (PyObject *self, enum gdb
>  {
>   typy_iterator_object *typy_iter_obj;
>
> +  /* Check that "self" is a structure or union type.  */
> +  if (typy_deref (((type_object *) self)->type) == NULL)
> +    return NULL;
> +
>   typy_iter_obj = PyObject_New (typy_iterator_object,
>                                &type_iterator_object_type);
>   if (typy_iter_obj == NULL)
> @@ -1499,6 +1536,32 @@ Return a volatile variant of this type"
>   { NULL }
>  };
>
> +static PyNumberMethods type_object_as_number = {
> +  NULL,                              /* nb_add */
> +  NULL,                              /* nb_subtract */
> +  NULL,                              /* nb_multiply */
> +  NULL,                              /* nb_divide */
> +  NULL,                              /* nb_remainder */
> +  NULL,                              /* nb_divmod */
> +  NULL,                              /* nb_power */
> +  NULL,                              /* nb_negative */
> +  NULL,                              /* nb_positive */
> +  NULL,                              /* nb_absolute */
> +  typy_nonzero,                      /* nb_nonzero */
> +  NULL,                              /* nb_invert */
> +  NULL,                              /* nb_lshift */
> +  NULL,                              /* nb_rshift */
> +  NULL,                              /* nb_and */
> +  NULL,                              /* nb_xor */
> +  NULL,                              /* nb_or */
> +  NULL,                              /* nb_coerce */
> +  NULL,                              /* nb_int */
> +  NULL,                              /* nb_long */
> +  NULL,                              /* nb_float */
> +  NULL,                              /* nb_oct */
> +  NULL                       /* nb_hex */
> +};
> +
>  static PyMappingMethods typy_mapping = {
>   typy_length,
>   typy_getitem,
> @@ -1518,7 +1581,7 @@ static PyTypeObject type_object_type =
>   0,                             /*tp_setattr*/
>   0,                             /*tp_compare*/
>   0,                             /*tp_repr*/
> -  0,                             /*tp_as_number*/
> +  &type_object_as_number,        /*tp_as_number*/
>   0,                             /*tp_as_sequence*/
>   &typy_mapping,                 /*tp_as_mapping*/
>   0,                             /*tp_hash */
>
>


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

* Re: [RFA] Python: raise exception on field-related gdb.Type methods if it's not struct or union
  2011-11-09  0:29                                 ` Doug Evans
@ 2011-11-09  1:53                                   ` Paul Koning
  2011-11-09 18:10                                   ` Paul Koning
  1 sibling, 0 replies; 26+ messages in thread
From: Paul Koning @ 2011-11-09  1:53 UTC (permalink / raw)
  To: Doug Evans; +Cc: Eli Zaretskii, tromey, gdb-patches


On Nov 8, 2011, at 7:29 PM, Doug Evans wrote:

> On Tue, Nov 8, 2011 at 1:40 PM, Paul Koning <paulkoning@comcast.net> wrote:
>> ...
>> I found the answer to that question with a bit of searching.   Attached is a patch that raises an exception (TypeError) for len() and all the field reference methods if the type isn't struct or union.
> 
> I wasn't sure using as_number here was kosher.  "works for me" if it
> does the job.

Yes, the book (Python Reference) says that if __nonzero__ is defined (which is what this does) then that is used for truth testing, otherwise __len__ is, and if neither is defined then the thing in question is always True.

> 
>> Does this need new testcases?
> 
> Yeah, I think so.
> Verify "not type" DTRT, and affected operations on scalar types flag
> the right error.
> 
> One question I have is: for struct types that have an empty field list
> (say an empty struct), what's the result of "not type".  True or
> False?  I can argue either way, and I'm not sure what's the best
> answer, long term.
> [But then again, my python fu isn't enough to be comfortable with any
> conclusion I reach.]

The way I wrote it, a truth test on a gdb.Type object is always True, so the answer to the question is "False".  It doesn't matter if the type is scalar, or struct, or for struct whether it has fields or not.  The Python Reference says that objects that define neither a nonzero nor a len operation are always True.  That applies, for example, to the Python builtin object "type", and since gdb.Type is vaguely like "type" I figured the same behavior should apply.  
> 
> Thanks very much for doing this!
> 
> btw, a couple of nits:
> 1) Please put a blank line between a function's comment and its definition.
> There are several occurrences of this.

Oops, yes, I should have remembered that.
> 2) Can you rename typy_deref?  typy_get_struct works for me, I'm sure
> there's a better name.
>    "deref" feels confusing, one can deref a pointer to anything, not
> just structs/unions, and
>    the function doesn't necessarily do a dereference.

I like the name you mentioned.  Will do.

	paul


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

* Re: [RFA] Python: raise exception on field-related gdb.Type methods if it's not struct or union
  2011-11-09  0:29                                 ` Doug Evans
  2011-11-09  1:53                                   ` Paul Koning
@ 2011-11-09 18:10                                   ` Paul Koning
  2011-11-15 19:01                                     ` [PING] " Paul Koning
  1 sibling, 1 reply; 26+ messages in thread
From: Paul Koning @ 2011-11-09 18:10 UTC (permalink / raw)
  To: Doug Evans; +Cc: Eli Zaretskii, tromey, gdb-patches

This got a bit more complicated once I looked at the testcases.

First, it's not just struct and union types that have fields; enum types also do.  So the common code that checks for valid types has to allow those three.

Second, the fields method is also defined for array types, where it returns a list with a single entry that's a range type.  I made that a special case since everything else doesn't apply to array types.

Attached is an updated patch including new test cases.

	paul

ChangeLog:

2011-11-09  Paul Koning  <paul_koning@dell.com>

	* python/py-type.c (typy_get_composite): New function.
	(typy_nonzero): New function.
	(typy_values): Rename from typy_fields.
	(typy_fields): New function.
	(typy_length): Raise exception if not struct, union, or enum type.
	(typy_getitem): Ditto.
	(typy_has_key): Ditto.
	(typy_make_iter): Ditto.

testsuite/ChangeLog:

2011-11-09  Paul Koning  <paul_koning@dell.com>

	* gdb.python/py-type.exp: New testcases for exceptions on scalar
	types.

Index: gdb/python/py-type.c
===================================================================
RCS file: /cvs/src/src/gdb/python/py-type.c,v
retrieving revision 1.28
diff -u -r1.28 py-type.c
--- gdb/python/py-type.c	4 Nov 2011 11:57:04 -0000	1.28
+++ gdb/python/py-type.c	9 Nov 2011 17:36:12 -0000
@@ -308,11 +308,41 @@
 /* Return a sequence of all fields.  Each field is a gdb.Field object.  */
 
 static PyObject *
-typy_fields (PyObject *self, PyObject *args)
+typy_values (PyObject *self, PyObject *args)
 {
   return typy_fields_items (self, iter_values);
 }
 
+/* Return a sequence of all fields.  Each field is a gdb.Field object.
+   This method is similar to typy_values, except where the supplied 
+   gdb.Type is an array, in which case it returns a list of one entry
+   which is a gdb.Field object for a range (the array bounds).  */
+
+static PyObject *
+typy_fields (PyObject *self, PyObject *args)
+{
+  struct type *type = ((type_object *) self)->type;
+  PyObject *r, *rl;
+  
+  if (TYPE_CODE (type) != TYPE_CODE_ARRAY)
+    return typy_fields_items (self, iter_values);
+
+  /* Array type.  Handle this as a special case because the common
+     machinery wants struct or union or enum types.  Build a list of
+     one entry which is the range for the array.  */
+  r = convert_field (type, 0);
+  if (r == NULL)
+    return NULL;
+  
+  rl = Py_BuildValue ("[O]", r);
+  if (rl == NULL)
+    {
+      Py_DECREF (r);
+    }
+
+  return rl;
+}
+
 /* Return a sequence of all field names.  Each field is a gdb.Field object.  */
 
 static PyObject *
@@ -350,6 +380,48 @@
   return type_to_type_object (check_typedef (type));
 }
 
+/* Strip typedefs and pointers/reference from a type.  Then check that
+   it is a struct, union, or enum type.  If not, raise TypeError.  */
+
+static struct type *
+typy_get_composite (struct type *type)
+{
+  volatile struct gdb_exception except;
+
+  for (;;)
+    {
+      TRY_CATCH (except, RETURN_MASK_ALL)
+	{
+	  CHECK_TYPEDEF (type);
+	}
+      /* Don't use GDB_PY_HANDLE_EXCEPTION here because that returns
+	 a (NULL) pointer of the wrong type.  */
+      if (except.reason < 0)
+	{
+	  gdbpy_convert_exception (except);
+	  return NULL;
+	}
+
+      if (TYPE_CODE (type) != TYPE_CODE_PTR
+	  && TYPE_CODE (type) != TYPE_CODE_REF)
+	break;
+      type = TYPE_TARGET_TYPE (type);
+    }
+
+  /* If this is not a struct, union, or enum type, raise TypeError
+     exception.  */
+  if (TYPE_CODE (type) != TYPE_CODE_STRUCT 
+      && TYPE_CODE (type) != TYPE_CODE_UNION
+      && TYPE_CODE (type) != TYPE_CODE_ENUM)
+    {
+      PyErr_SetString (PyExc_TypeError,
+		       "Type is not a structure, union, or enum type.");
+      return NULL;
+    }
+  
+  return type;
+}
+
 /* Return an array type.  */
 
 static PyObject *
@@ -1124,9 +1196,23 @@
 {
   struct type *type = ((type_object *) self)->type;
 
+  type = typy_get_composite (type);
+  if (type == NULL)
+    return -1;
+
   return TYPE_NFIELDS (type);
 }
 
+/* Implements boolean evaluation of gdb.Type.  Handle this like other
+   Python objects that don't have a meaningful truth value -- all 
+   values are true.  */
+
+static int
+typy_nonzero (PyObject *self)
+{
+  return 1;
+}
+
 /* Return a gdb.Field object for the field named by the argument.  */
 
 static PyObject *
@@ -1145,20 +1231,10 @@
      using lookup_struct_elt_type, portions of that function are
      copied here.  */
 
-  for (;;)
-    {
-      TRY_CATCH (except, RETURN_MASK_ALL)
-	{
-	  CHECK_TYPEDEF (type);
-	}
-      GDB_PY_HANDLE_EXCEPTION (except);
-
-      if (TYPE_CODE (type) != TYPE_CODE_PTR
-	  && TYPE_CODE (type) != TYPE_CODE_REF)
-	break;
-      type = TYPE_TARGET_TYPE (type);
-    }
-
+  type = typy_get_composite (type);
+  if (type == NULL)
+    return NULL;
+  
   for (i = 0; i < TYPE_NFIELDS (type); i++)
     {
       char *t_field_name = TYPE_FIELD_NAME (type, i);
@@ -1216,18 +1292,9 @@
      using lookup_struct_elt_type, portions of that function are
      copied here.  */
 
-  for (;;)
-    {
-      TRY_CATCH (except, RETURN_MASK_ALL)
-	{
-	  CHECK_TYPEDEF (type);
-	}
-      GDB_PY_HANDLE_EXCEPTION (except);
-      if (TYPE_CODE (type) != TYPE_CODE_PTR
-	  && TYPE_CODE (type) != TYPE_CODE_REF)
-	break;
-      type = TYPE_TARGET_TYPE (type);
-    }
+  type = typy_get_composite (type);
+  if (type == NULL)
+    return NULL;
 
   for (i = 0; i < TYPE_NFIELDS (type); i++)
     {
@@ -1246,6 +1313,10 @@
 {
   typy_iterator_object *typy_iter_obj;
 
+  /* Check that "self" is a structure or union type.  */
+  if (typy_get_composite (((type_object *) self)->type) == NULL)
+    return NULL;
+  
   typy_iter_obj = PyObject_New (typy_iterator_object,
 				&type_iterator_object_type);
   if (typy_iter_obj == NULL)
@@ -1489,7 +1560,7 @@
   { "unqualified", typy_unqualified, METH_NOARGS,
     "unqualified () -> Type\n\
 Return a variant of this type without const or volatile attributes." },
-  { "values", typy_fields, METH_NOARGS,
+  { "values", typy_values, METH_NOARGS,
     "values () -> list\n\
 Return a list holding all the fields of this type.\n\
 Each field is a gdb.Field object." },
@@ -1499,6 +1570,32 @@
   { NULL }
 };
 
+static PyNumberMethods type_object_as_number = {
+  NULL,			      /* nb_add */
+  NULL,			      /* nb_subtract */
+  NULL,			      /* nb_multiply */
+  NULL,			      /* nb_divide */
+  NULL,			      /* nb_remainder */
+  NULL,			      /* nb_divmod */
+  NULL,			      /* nb_power */
+  NULL,			      /* nb_negative */
+  NULL,			      /* nb_positive */
+  NULL,			      /* nb_absolute */
+  typy_nonzero,		      /* nb_nonzero */
+  NULL,			      /* nb_invert */
+  NULL,			      /* nb_lshift */
+  NULL,			      /* nb_rshift */
+  NULL,			      /* nb_and */
+  NULL,			      /* nb_xor */
+  NULL,			      /* nb_or */
+  NULL,			      /* nb_coerce */
+  NULL,			      /* nb_int */
+  NULL,			      /* nb_long */
+  NULL,			      /* nb_float */
+  NULL,			      /* nb_oct */
+  NULL			      /* nb_hex */
+};
+
 static PyMappingMethods typy_mapping = {
   typy_length,
   typy_getitem,
@@ -1518,7 +1615,7 @@
   0,				  /*tp_setattr*/
   0,				  /*tp_compare*/
   0,				  /*tp_repr*/
-  0,				  /*tp_as_number*/
+  &type_object_as_number,	  /*tp_as_number*/
   0,				  /*tp_as_sequence*/
   &typy_mapping,		  /*tp_as_mapping*/
   0,				  /*tp_hash */
Index: gdb/testsuite/gdb.python/py-type.exp
===================================================================
RCS file: /cvs/src/src/gdb/testsuite/gdb.python/py-type.exp,v
retrieving revision 1.14
diff -u -r1.14 py-type.exp
--- gdb/testsuite/gdb.python/py-type.exp	28 Sep 2011 20:06:01 -0000	1.14
+++ gdb/testsuite/gdb.python/py-type.exp	9 Nov 2011 17:36:13 -0000
@@ -93,6 +93,16 @@
     gdb_test "python print \[(n, v.bitpos) for (n, v) in st.type.items()\]" {\[\('a', 0L\), \('b', 32L\)\]} "Check fields items list"
   gdb_test "python print 'a' in st.type" "True" "Check field name exists test"
   gdb_test "python print 'nosuch' in st.type" "False" "Check field name nonexists test"
+  gdb_test "python print not not st.type" "True" "Check conversion to bool"
+
+  # Test rejection of mapping operations on scalar types
+  gdb_test "python print len (st.type\['a'\].type)" "TypeError: Type is not a structure, union, or enum type.*"
+  gdb_test "python print st.type\['a'\].type.has_key ('x')" "TypeError: Type is not a structure, union, or enum type.*"
+  gdb_test "python print st.type\['a'\].type.keys ()" "TypeError: Type is not a structure, union, or enum type.*"
+  gdb_test "python print st.type\['a'\].type\['x'\]" "TypeError: Type is not a structure, union, or enum type.*"
+
+  # Test conversion to bool on scalar types
+  gdb_test "python print not not st.type\['a'\].type" "True"
   
   # Test regression PR python/10805
   gdb_py_test_silent_cmd "print ar" "print value" 1


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

* [PING] Re: [RFA] Python: raise exception on field-related gdb.Type methods if it's not struct or union
  2011-11-09 18:10                                   ` Paul Koning
@ 2011-11-15 19:01                                     ` Paul Koning
  2011-11-15 20:57                                       ` Doug Evans
  0 siblings, 1 reply; 26+ messages in thread
From: Paul Koning @ 2011-11-15 19:01 UTC (permalink / raw)
  To: gdb-patches

Ping...

http://sourceware.org/ml/gdb-patches/2011-11/msg00241.html

	paul


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

* Re: [PING] Re: [RFA] Python: raise exception on field-related gdb.Type methods if it's not struct or union
  2011-11-15 19:01                                     ` [PING] " Paul Koning
@ 2011-11-15 20:57                                       ` Doug Evans
  2011-11-15 21:21                                         ` Paul Koning
  0 siblings, 1 reply; 26+ messages in thread
From: Doug Evans @ 2011-11-15 20:57 UTC (permalink / raw)
  To: Paul Koning; +Cc: gdb-patches

On Tue, Nov 15, 2011 at 11:00 AM, Paul Koning <paulkoning@comcast.net> wrote:
> Ping...
>
> http://sourceware.org/ml/gdb-patches/2011-11/msg00241.html

LGTM.  Thanks!


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

* Re: [PING] Re: [RFA] Python: raise exception on field-related gdb.Type methods if it's not struct or union
  2011-11-15 20:57                                       ` Doug Evans
@ 2011-11-15 21:21                                         ` Paul Koning
  0 siblings, 0 replies; 26+ messages in thread
From: Paul Koning @ 2011-11-15 21:21 UTC (permalink / raw)
  To: Doug Evans; +Cc: gdb-patches


On Nov 15, 2011, at 3:56 PM, Doug Evans wrote:

> On Tue, Nov 15, 2011 at 11:00 AM, Paul Koning <paulkoning@comcast.net> wrote:
>> Ping...
>> 
>> http://sourceware.org/ml/gdb-patches/2011-11/msg00241.html
> 
> LGTM.  Thanks!

Thanks.  Committed.

	paul


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

end of thread, other threads:[~2011-11-15 21:21 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-09-16  4:09 Python: add field access by name and standard python mapping methods to gdb.Type Paul Koning
2011-09-16  7:36 ` Eli Zaretskii
2011-09-16 10:47 ` Phil Muldoon
2011-09-16 14:57   ` Paul Koning
2011-09-16 14:57     ` Phil Muldoon
2011-09-16 15:41   ` Paul Koning
2011-09-23 17:21     ` Doug Evans
2011-09-26 17:15       ` [RFA] " Paul Koning
2011-09-28 19:25         ` Doug Evans
2011-09-28 20:41           ` Paul Koning
2011-10-04 15:14             ` Tom Tromey
2011-10-04 15:30               ` Paul Koning
2011-10-04 15:42                 ` Eli Zaretskii
2011-10-04 15:56                   ` Paul Koning
2011-11-04 17:42                     ` Doug Evans
2011-11-04 20:41                       ` Paul Koning
2011-11-04 23:05                         ` Doug Evans
2011-11-05 14:36                           ` Paul Koning
2011-11-05 21:04                             ` Doug Evans
2011-11-08 21:40                               ` [RFA] Python: raise exception on field-related gdb.Type methods if it's not struct or union Paul Koning
2011-11-09  0:29                                 ` Doug Evans
2011-11-09  1:53                                   ` Paul Koning
2011-11-09 18:10                                   ` Paul Koning
2011-11-15 19:01                                     ` [PING] " Paul Koning
2011-11-15 20:57                                       ` Doug Evans
2011-11-15 21:21                                         ` Paul Koning

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox