[RFC v2 05/21] gdb/python: add function () method to gdb.Type object
Jan Vraný
Jan.Vrany@labware.com
Tue Dec 10 22:02:49 GMT 2024
On Mon, 2024-12-09 at 14:56 +0000, Andrew Burgess wrote:
> Jan Vrany <jan.vrany@labware.com> writes:
>
> > This commit adds a new method to Python type objects that returns
> > possibly new function type returning that type. Parameter types can
> > be specified too.
> >
> > This will be useful later to create types for function symbols
> > created
> > using Python extension code.
> >
> > Reviewed-By: Eli Zaretskii <eliz@gnu.org>
> > ---
> > gdb/NEWS | 3 ++
> > gdb/doc/python.texi | 8 +++++
> > gdb/python/py-type.c | 54
> > ++++++++++++++++++++++++++++
> > gdb/testsuite/gdb.python/py-type.exp | 20 +++++++++++
> > 4 files changed, 85 insertions(+)
> >
> > diff --git a/gdb/NEWS b/gdb/NEWS
> > index 765d14a1ae4..3d208744103 100644
> > --- a/gdb/NEWS
> > +++ b/gdb/NEWS
> > @@ -91,6 +91,9 @@
> > ** Added gdb.Architecture.void_type. Returns a gdb.Type
> > representing "void"
> > type for that architecture.
> >
> > + ** Added gdb.Type.function. Returns a new gdb.Type representing
> > a function
> > + returning that type. Parameter types can be specified too.
> > +
> > * Debugger Adapter Protocol changes
> >
> > ** The "scopes" request will now return a scope holding global
> > diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
> > index dac5115a5f8..272b51d32c5 100644
> > --- a/gdb/doc/python.texi
> > +++ b/gdb/doc/python.texi
> > @@ -1574,6 +1574,14 @@ Return a new @code{gdb.Type} object which
> > represents a pointer to this
> > type.
> > @end defun
> >
> > +@defun Type.function (@r{[}param_type...@r{]})
> > +Return a new @code{gdb.Type} object which represents a type of
> > function
> > +returning this type. @code{param_type...} arguments specify
> > parameter
>
> Shouldn't this be: @var{param_type...} instead of @code? The
> `param_type' isn't an actual piece of code, but a reference to a
> placeholder in the function template above?
Thanks. Will fix it next submission.
>
> > +types. Use @code{None} as last parameter type to create a vararg
> > function
> > +type. When invoked with single @code{None} argument or with no
> > arguments at
> > +all it creates a vararg function taking zero or more parameters.
>
> How do we create a function which takes no arguments then? I think
> the
> single `None` case is different from the empty list case, right?
Yes, you're right. It should be and it is. I've got fooled by the
printing behaviour you pointed out below.
>
> With this patch applied I tried this:
>
> (gdb) python arch = gdb.selected_inferior().architecture()
> (gdb) python int_t = arch.integer_type(8, True)
> (gdb) python func_t = int_t.function()
> (gdb) python print(func_t)
> int8_t ()
> (gdb)
>
> Which looks like a function that takes no arguments. So then I
> tried:
>
> (gdb) python func_t = int_t.function(None)
> (gdb) python print(func_t)
> int8_t ()
> (gdb)
>
> Which looks like I go the same.
Yes, both *look* the same but are not. To double check I added
gdb.Type.has_varargs (returning type::has_varargs()) and it
returned False in first case and True in second.
I'll fix the documentation.
> So I checked
> lookup_function_type_with_arguments, and indeed, a trailing nullptr
> does
> make this a vararg function. It feels like we should probably be
> printing the `...` in the type signature. I guess this is a bug
> somewhere, likely because C doesn't support varargs without a named
> argument first, so we've never tried to print such a thing before.
> It
> would be nice to get this fixed (likely not in this patch though),
> but
> at the very least we should create a new sourceware bug to track this
> issue.
I'm not sure if it's a bug or feature. I single-stepped through the
printing machinery and the execution ended up in c_type_print_args.
There's a comment saying:
/* Print out a trailing ellipsis for varargs functions. Ignore
TYPE_VARARGS if the function has no named arguments; that
represents unprototyped (K&R style) C functions. */
But reading that method I'm even more confused. Shall
gdb.Type.function() make the type "prototyped", that is, call
type::set_is_prototyped(true)? Anyways, I will probably open a bug
as suggested.
>
> Additionally, instead of using `None` for the special value, I wonder
> if
> it would be nicer to use an integer constant. I'm not sure exactly
> what
> we'd call it, but something like this:
>
> (gdb) python func_t = int_t.function(int_t, gdb.VARARGS)
>
> This might be more self-documenting, than using `None`. What do you
> think?
Yes, I like it. Maybe using object() as the value and not int.
>
> > +@end defun
> > +
> > @defun Type.strip_typedefs ()
> > Return a new @code{gdb.Type} that represents the real type,
> > after removing all layers of typedefs.
> > diff --git a/gdb/python/py-type.c b/gdb/python/py-type.c
> > index 284960a3a87..348889dddd8 100644
> > --- a/gdb/python/py-type.c
> > +++ b/gdb/python/py-type.c
> > @@ -774,6 +774,57 @@ typy_unqualified (PyObject *self, PyObject
> > *args)
> > return type_to_type_object (type);
> > }
> >
> > +/* Return a function type. */
> > +static PyObject *
> > +typy_function (PyObject *self, PyObject *args)
> > +{
> > + struct type *type = ((type_object *) self)->type;
> > +
> > + gdb_assert (PySequence_Check (args));
> > +
> > + std::vector<struct type *> param_types (PySequence_Length
> > (args));
> > +
> > + for (int i = 0; i < PySequence_Length (args); i++)
> > + {
> > + PyObject *param_type_obj = PySequence_GetItem (args, i);
>
> PySequence_GetItem can return nullptr on error, you need to check for
> this case.
OK.
>
> > +
> > + if (param_type_obj == Py_None)
> > + {
> > + param_types[i] = nullptr;
> > + if (i != (PySequence_Length (args) - 1))
> > + {
> > + PyErr_Format (PyExc_ValueError,
> > + _("Argument at index %d is None but
> > None can "
> > + "only be the last type."), i);
> > + return nullptr;
> > + }
> > + }
> > + else
> > + {
> > + param_types[i] = type_object_to_type (param_type_obj);
> > + if (!param_types[i])
>
> Should be: if (param_types[i] == nullptr)
>
OK.
> > + {
> > + PyErr_Format (PyExc_TypeError,
> > + _("Argument at index %d is not a
> > gdb.Type "
> > + "object."), i);
>
> This error could be improved with something like:
>
> PyErr_Format (PyExc_TypeError,
> _("Argument at index %d is %s, not a gdb.Type "
> "object."), i, Py_TYPE (param_type_obj)-
> >tp_name);
>
> The builtin Python errors related to e.g. function argument
> processing,
> tend to show the incorrect type, and I think it helps. And it's
> pretty
> easy to do.
Good idea, will do.
>
> > + return nullptr;
> > + }
> > + }
> > + }
> > +
> > + try
> > + {
> > + type = lookup_function_type_with_arguments (
> > + type, param_types.size (), param_types.data ());
>
> Please wrap lines before the '(', not immediately after.
>
>
OK. I saw it written like this elsewhere in the code.
Thanks for looking into this!
Jan
> Thanks,
> Andrew
>
> > + }
> > + catch (const gdb_exception &except)
> > + {
> > + return gdbpy_handle_gdb_exception (nullptr, except);
> > + }
> > +
> > + return type_to_type_object (type);
> > +}
> > +
> > /* Return the size of the type represented by SELF, in bytes. */
> > static PyObject *
> > typy_get_sizeof (PyObject *self, void *closure)
> > @@ -1641,6 +1692,9 @@ Return the type of a template argument." },
> > { "unqualified", typy_unqualified, METH_NOARGS,
> > "unqualified () -> Type\n\
> > Return a variant of this type without const or volatile
> > attributes." },
> > + { "function", typy_function, METH_VARARGS,
> > + "function () -> Type\n\
> > +Return a function type returning value of this type." },
> > { "values", typy_values, METH_NOARGS,
> > "values () -> list\n\
> > Return a list holding all the fields of this type.\n\
> > diff --git a/gdb/testsuite/gdb.python/py-type.exp
> > b/gdb/testsuite/gdb.python/py-type.exp
> > index 7e469c93c35..783261e6f4a 100644
> > --- a/gdb/testsuite/gdb.python/py-type.exp
> > +++ b/gdb/testsuite/gdb.python/py-type.exp
> > @@ -365,6 +365,26 @@ if { [build_inferior "${binfile}" "c"] == 0 }
> > {
> > gdb_test "python print(gdb.lookup_type('int').optimized_out())"
> > \
> > "<optimized out>"
> >
> > + gdb_test_no_output "python int_t = gdb.lookup_type('int')"
> > +
> > + gdb_test "python print(repr(int_t.function()))" \
> > + "<gdb.Type code=TYPE_CODE_FUNC name=int \\(\\)>"
> > +
> > + gdb_test "python print(repr(int_t.function(int_t, int_t,
> > int_t)))" \
> > + "<gdb.Type code=TYPE_CODE_FUNC name=int \\(int, int,
> > int\\)>"
> > +
> > + gdb_test "python print(repr(int_t.function(int_t, None)))" \
> > + "<gdb.Type code=TYPE_CODE_FUNC name=int \\(int, ...\\)>"
> > +
> > + gdb_test "python print(repr(int_t.function(None)))" \
> > + "<gdb.Type code=TYPE_CODE_FUNC name=int \\(\\)>"
> > +
> > + gdb_test "python print(repr(int_t.function(123)))" \
> > + "TypeError.*:.*"
> > +
> > + gdb_test "python print(repr(int_t.function(int_t, None,
> > int_t)))" \
> > + "ValueError.*:.*"
> > +
> > set sint [get_sizeof int 0]
> > gdb_test "python
> > print(gdb.parse_and_eval('aligncheck').type.alignof)" \
> > $sint
> > --
> > 2.45.2
>
More information about the Gdb-patches
mailing list