Re: [RFA] Python: raise exception on field-related gdb.Type methods if it's not struct or union

Paul Koning
2011-11-09T18:10:51Z

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