[PATCHv3 2/2] gdb/python: add Type.signedness attribute
Andrew Burgess
aburgess@redhat.com
Wed Dec 8 10:19:26 GMT 2021
* Andrew Burgess <aburgess@redhat.com> [2021-12-08 09:56:57 +0000]:
> * Simon Marchi <simon.marchi@polymtl.ca> [2021-12-07 22:13:40 -0500]:
>
> > > +@vindex TYPE_SIGNEDNESS_NONE
> > > +@item gdb.TYPE_SIGNEDNESS_NONE
> > > +This type is considered neither signed, or unsigned. This applies to
> > > +all non-scalar types (e.g.@: c language structs), but can sometimes be
> > > +the value return for scalar type, one example of where this can be
> > > +seen is in C++, where @code{char}, @code{signed char}, and
> > > +@code{unsigned char} are all considered distinct types.
> >
> > I'm curious, why would type `unsigned char` not return
> > gdb.TYPE_SIGNEDNESS_UNSIGNED? I see the corresponding code in
> > gdbtypes.c, with the m_flag_nosign flag, so I understand that you would
> > implementation the Python binding based on that, but I am wondering
> > about the historical reason.
>
> Sorry, but I don't understand the question. I'm assuming you actually
> meant to ask a slightly different question, but, just for clarity,
> I'll answer your actual question first. I added these declarations to
> gdb.python/py-type.c:
>
> unsigned char global_unsigned_char;
> char global_char;
> signed char global_signed_char;
>
> And these tests to gdb.python/py-type.exp:
>
> gdb_test "python print (gdb.parse_and_eval ('global_unsigned_char').type.signedness == gdb.TYPE_SIGNEDNESS_UNSIGNED)" "True"
> gdb_test "python print (gdb.parse_and_eval ('global_char').type.signedness == gdb.TYPE_SIGNEDNESS_NONE)" "True"
> gdb_test "python print (gdb.parse_and_eval ('global_signed_char').type.signedness == gdb.TYPE_SIGNEDNESS_SIGNED)" "True"
>
> So you can see that `unsigned char` does return
> gdb.TYPE_SIGNEDNESS_UNSIGNED.
>
> If your question is why would `char` not return UNSIGNED, then it
> appears that plain char is not defined as unsigned in the standard.
> This appears to be described in 6.2.5/15 of the C language draft that
> I have to hand:
>
> "The three types char, signed char, and unsigned char are
> collectively called the character types. The implementation shall
> define char to have the same range, representation, and behavior as
> either signed char or unsigned char.)"
>
> But I guess the problem is that whether char is signed or unsigned is
> not encoded into the DWARF.
>
> I'll update the Python documentation to talk about C/C++ rather than
> just C++ w.r.t. this state.
Below is an updated version of this patch. The changes from v2 are:
- Updated the docs to talk about C and C++ now,
- Run the char tests on both C and C++.
Given the changes are so minor, I'm considering this reviewed from the
engineering side. I'll give Eli a few more days for a docs review,
then merge this.
Thanks,
Andrew
---
commit d492d88c50273b0af8f1d26d1706db5455bcfd8f
Author: Andrew Burgess <aburgess@redhat.com>
Date: Tue Nov 30 14:35:44 2021 +0000
gdb/python: add Type.signedness attribute
Add a new gdb.Type.signedness attribute that describes whether a type
is signed or not.
Unfortunately types can be signed, unsigned, or neither, so this
attribute is not a boolean, but is instead a integer that takes the
value of one of three new constants:
gdb.TYPE_SIGNEDNESS_SIGNED
gdb.TYPE_SIGNEDNESS_UNSIGNED
gdb.TYPE_SIGNEDNESS_NONE
There's documentation, NEWS, and a few tests for the new
functionality.
diff --git a/gdb/NEWS b/gdb/NEWS
index 13b66286876..887530364c4 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -94,6 +94,10 @@ maint packet
is equivalent to the existing 'maint packet' CLI command; it
allows a user specified packet to be sent to the remote target.
+ ** New read-only attribute gdb.Type.signedness, which contains one
+ of three new constants, gdb.TYPE_SIGNEDNESS_UNSIGNED,
+ gdb.TYPE_SIGNEDNESS_SIGNED, or gdb.TYPE_SIGNEDNESS_NONE.
+
* New features in the GDB remote stub, GDBserver
** GDBserver is now supported on OpenRISC GNU/Linux.
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index 4a66c11c19d..423d0f8b23a 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -1211,6 +1211,30 @@
there is no associated objfile.
@end defvar
+@defvar Type.signedness
+A constant describing the signedness of this type. The valid values
+for this field are defined in the @code{gdb} module:
+
+@vtable @code
+@vindex TYPE_SIGNEDNESS_UNSIGNED
+@item gdb.TYPE_SIGNEDNESS_UNSIGNED
+This is an unsigned type.
+
+@vindex TYPE_SIGNEDNESS_SIGNED
+@item gdb.TYPE_SIGNEDNESS_SIGNED
+This is a signed type.
+
+@vindex TYPE_SIGNEDNESS_NONE
+@item gdb.TYPE_SIGNEDNESS_NONE
+This type is considered neither signed, or unsigned. This applies to
+all non-scalar types (e.g.@: C language structs), but can sometimes be
+the value return for scalar type, one example of where this can be
+seen is in C and C++, where @code{char}, @code{signed char}, and
+@code{unsigned char} are all considered distinct types.
+
+@end vtable
+@end defvar
+
The following methods are provided:
@defun Type.fields ()
diff --git a/gdb/python/py-type.c b/gdb/python/py-type.c
index 15d73fe94e4..f33833bda58 100644
--- a/gdb/python/py-type.c
+++ b/gdb/python/py-type.c
@@ -115,6 +115,40 @@ static struct pyty_code pyty_codes[] =
ENTRY (TYPE_CODE_INTERNAL_FUNCTION),
};
+#undef ENTRY
+
+/* Constants representing the possible signedness states. Every type is
+ in one of these states. */
+
+enum py_type_signedness
+{
+ PY_TYPE_SIGNEDNESS_SIGNED,
+ PY_TYPE_SIGNEDNESS_UNSIGNED,
+ PY_TYPE_SIGNEDNESS_NONE,
+};
+
+/* This is used to initialize the gdb.TYPE_SIGNEDNESS_ constants. */
+
+struct pyty_signedness
+{
+ /* The code. */
+ enum py_type_signedness signedness;
+ /* The name. */
+ const char *name;
+};
+
+/* A table mapping the PY_TYPE_SIGNEDNESS_ constants onto a string that
+ will be exported into Python as a constant in the gdb module. */
+
+static struct pyty_signedness pyty_signedness_constants[] =
+{
+#define ENTRY(X) { PY_ ## X, #X }
+ ENTRY (TYPE_SIGNEDNESS_SIGNED),
+ ENTRY (TYPE_SIGNEDNESS_UNSIGNED),
+ ENTRY (TYPE_SIGNEDNESS_NONE),
+#undef ENTRY
+};
+
static void
@@ -433,6 +467,24 @@ typy_get_objfile (PyObject *self, void *closure)
return objfile_to_objfile_object (objfile).release ();
}
+/* Return the signedness constant for this type. */
+
+static PyObject *
+typy_get_signedness (PyObject *self, void *closure)
+{
+ struct type *type = ((type_object *) self)->type;
+
+ enum py_type_signedness signedness;
+ if (!is_scalar_type (type) || type->has_no_signedness ())
+ signedness = PY_TYPE_SIGNEDNESS_NONE;
+ else if (type->is_unsigned ())
+ signedness = PY_TYPE_SIGNEDNESS_UNSIGNED;
+ else
+ signedness = PY_TYPE_SIGNEDNESS_SIGNED;
+
+ return gdb_py_object_from_longest (signedness).release ();
+}
+
/* Return the type, stripped of typedefs. */
static PyObject *
typy_strip_typedefs (PyObject *self, PyObject *args)
@@ -1456,6 +1508,13 @@ gdbpy_initialize_types (void)
return -1;
}
+ /* Place the gdb.TYPE_SIGNEDNESS_ constants into the gdb module. */
+ for (const auto &item : pyty_signedness_constants)
+ {
+ if (PyModule_AddIntConstant (gdb_module, item.name, item.signedness) < 0)
+ return -1;
+ }
+
if (gdb_pymodule_addobject (gdb_module, "Type",
(PyObject *) &type_object_type) < 0)
return -1;
@@ -1486,6 +1545,8 @@ static gdb_PyGetSetDef type_object_getset[] =
"The tag name for this type, or None.", NULL },
{ "objfile", typy_get_objfile, NULL,
"The objfile this type was defined in, or None.", NULL },
+ { "signedness", typy_get_signedness, NULL,
+ "The signedness for this type.", NULL },
{ NULL }
};
diff --git a/gdb/testsuite/gdb.python/py-arch.exp b/gdb/testsuite/gdb.python/py-arch.exp
index 14dc1bf85ee..a919544f064 100644
--- a/gdb/testsuite/gdb.python/py-arch.exp
+++ b/gdb/testsuite/gdb.python/py-arch.exp
@@ -64,12 +64,25 @@ if { ![is_address_zero_readable] } {
}
foreach size {0 1 2 3 4 8 16} {
- foreach sign {"" ", True" ", False" ", None" ", \"blah\""} {
+ foreach sign_data {{"" TYPE_SIGNEDNESS_SIGNED} \
+ {", True" TYPE_SIGNEDNESS_SIGNED} \
+ {", False" TYPE_SIGNEDNESS_UNSIGNED} \
+ {", None" TYPE_SIGNEDNESS_UNSIGNED} \
+ {", \"blah\"" TYPE_SIGNEDNESS_SIGNED}} {
+ set sign [lindex $sign_data 0]
+ # GDB's 0 bit type is always signed.
+ if { $size == 0 } {
+ set sign_result TYPE_SIGNEDNESS_SIGNED
+ } else {
+ set sign_result [lindex $sign_data 1]
+ }
set fullsize [expr 8 * $size]
gdb_test_no_output "python t = arch.integer_type($fullsize$sign)" \
"get integer type for $size$sign"
gdb_test "python print(t.sizeof)" "$size" \
"print size of integer type for $size$sign"
+ gdb_test "python print(t.signedness == gdb.${sign_result})" "True" \
+ "check signedness of type for $size$sign"
}
}
diff --git a/gdb/testsuite/gdb.python/py-type.c b/gdb/testsuite/gdb.python/py-type.c
index a599d0c901e..d73dad37e0e 100644
--- a/gdb/testsuite/gdb.python/py-type.c
+++ b/gdb/testsuite/gdb.python/py-type.c
@@ -32,6 +32,13 @@ TS ts;
int aligncheck;
+union UU
+{
+ int i;
+ float f;
+ int a[5];
+};
+
#ifdef __cplusplus
struct C
{
@@ -72,6 +79,10 @@ Temargs<D, 23, &C::c> temvar;
#endif
+unsigned char global_unsigned_char;
+char global_char;
+signed char global_signed_char;
+
enum E
{ v1, v2, v3
};
@@ -91,6 +102,7 @@ main ()
int ar[2] = {1,2};
struct s st;
struct SS ss;
+ union UU uu;
#ifdef __cplusplus
C c;
c.c = 1;
diff --git a/gdb/testsuite/gdb.python/py-type.exp b/gdb/testsuite/gdb.python/py-type.exp
index 733b25d3210..f0229f30a08 100644
--- a/gdb/testsuite/gdb.python/py-type.exp
+++ b/gdb/testsuite/gdb.python/py-type.exp
@@ -270,6 +270,28 @@ proc test_template {} {
gdb_test "python print (ttype.template_argument(2))" "&C::c"
}
+# Check the signedness of some types.
+proc test_signedness {lang} {
+ if {$lang == "c++"} {
+ gdb_test "python print (gdb.parse_and_eval ('c').type.signedness == gdb.TYPE_SIGNEDNESS_NONE)" "True"
+ gdb_test "python print (gdb.parse_and_eval ('&c').type.signedness == gdb.TYPE_SIGNEDNESS_UNSIGNED)" "True"
+ }
+
+ gdb_test "python print (gdb.parse_and_eval ('global_unsigned_char').type.signedness == gdb.TYPE_SIGNEDNESS_UNSIGNED)" "True"
+ gdb_test "python print (gdb.parse_and_eval ('global_char').type.signedness == gdb.TYPE_SIGNEDNESS_NONE)" "True"
+ gdb_test "python print (gdb.parse_and_eval ('global_signed_char').type.signedness == gdb.TYPE_SIGNEDNESS_SIGNED)" "True"
+
+ gdb_test "python print (gdb.parse_and_eval ('ss.x').type.signedness == gdb.TYPE_SIGNEDNESS_SIGNED)" "True"
+ gdb_test "python print (gdb.parse_and_eval ('ss').type.signedness == gdb.TYPE_SIGNEDNESS_NONE)" "True"
+ gdb_test "python print (gdb.parse_and_eval ('uu').type.signedness == gdb.TYPE_SIGNEDNESS_NONE)" "True"
+ gdb_test "python print (gdb.parse_and_eval ('uu.i').type.signedness == gdb.TYPE_SIGNEDNESS_SIGNED)" "True"
+ gdb_test "python print (gdb.parse_and_eval ('uu.f').type.signedness == gdb.TYPE_SIGNEDNESS_SIGNED)" "True"
+ gdb_test "python print (gdb.parse_and_eval ('uu.a').type.signedness == gdb.TYPE_SIGNEDNESS_NONE)" "True"
+
+ gdb_test "python print (gdb.parse_and_eval ('&ss.x').type.signedness == gdb.TYPE_SIGNEDNESS_UNSIGNED)" "True"
+ gdb_test "python print (gdb.parse_and_eval ('&uu').type.signedness == gdb.TYPE_SIGNEDNESS_UNSIGNED)" "True"
+}
+
# Perform C Tests.
if { [build_inferior "${binfile}" "c"] == 0 } {
restart_gdb "${binfile}"
@@ -292,10 +314,19 @@ if { [build_inferior "${binfile}" "c"] == 0 } {
gdb_test "python print(gdb.parse_and_eval('aligncheck').type.alignof)" \
$sint
+ # Ensure that all of the type signedness constants have different values.
+ gdb_test "python print (gdb.TYPE_SIGNEDNESS_SIGNED == gdb.TYPE_SIGNEDNESS_UNSIGNED)" \
+ "False"
+ gdb_test "python print (gdb.TYPE_SIGNEDNESS_SIGNED == gdb.TYPE_SIGNEDNESS_NONE)" \
+ "False"
+ gdb_test "python print (gdb.TYPE_SIGNEDNESS_UNSIGNED == gdb.TYPE_SIGNEDNESS_NONE)" \
+ "False"
+
with_test_prefix "lang_c" {
runto_bp "break to inspect struct and array."
test_fields "c"
test_enums
+ test_signedness "c"
}
}
@@ -310,5 +341,6 @@ if { [build_inferior "${binfile}-cxx" "c++"] == 0 } {
test_range
test_template
test_enums
+ test_signedness "c++"
}
}
More information about the Gdb-patches
mailing list