From: Jan Vrany <jan.vrany@labware.com>
To: gdb-patches@sourceware.org
Cc: Jan Vrany <jan.vrany@labware.com>, Eli Zaretskii <eliz@gnu.org>
Subject: [RFC v5 05/18] gdb/python: add function () method to gdb.Type object
Date: Mon, 23 Jun 2025 17:10:00 +0100 [thread overview]
Message-ID: <20250623161013.650814-6-jan.vrany@labware.com> (raw)
In-Reply-To: <20250623161013.650814-1-jan.vrany@labware.com>
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.
Unlike previous version, this commit copies all objfile-owned types
passed to gdb.Type.function to make them arch-owned. This way we do not
have to expose implementation details of type ownership to user.
This commit also prompted creation of type::printable_name in order to
produce nicer error message when user wants use types from different
architectures to create a new function type.
Reviewed-By: Eli Zaretskii <eliz@gnu.org>
---
gdb/NEWS | 3 +
gdb/doc/python.texi | 11 +++
gdb/gdbtypes.c | 20 ++++
gdb/gdbtypes.h | 6 ++
gdb/python/py-type.c | 132 +++++++++++++++++++++++++++
gdb/testsuite/gdb.python/py-type.exp | 36 ++++++++
6 files changed, 208 insertions(+)
diff --git a/gdb/NEWS b/gdb/NEWS
index 6c8a008d39d..e8e32a5dd5c 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -330,6 +330,9 @@ vFile:stat
** 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 6fa22851f6a..633526dc875 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -1594,6 +1594,17 @@ Return a new @code{gdb.Type} object which represents a pointer to this
type.
@end defun
+@defun Type.function (@r{[}param_type@dots{}@r{]}, @r{[}varargs@r{]})
+Return a new @code{gdb.Type} object which represents a type of function
+returning this type. Returned function type is always marked as prototyped.
+
+@var{param_type@dots{}} positional-only arguments specify parameter types.
+Passing @code{void} type is not allowed.
+
+The optional @var{varargs} keyword-only argument specifies whether to create
+vararg function type. Defaults to @code{False}.
+@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/gdbtypes.c b/gdb/gdbtypes.c
index 591a3cc4323..2dc5b7ff417 100644
--- a/gdb/gdbtypes.c
+++ b/gdb/gdbtypes.c
@@ -45,6 +45,7 @@
#include "rust-lang.h"
#include "ada-lang.h"
#include "extract-store-integer.h"
+#include "typeprint.h"
/* The value of an invalid conversion badness. */
#define INVALID_CONVERSION 100
@@ -5975,6 +5976,25 @@ type::is_array_like ()
return defn->is_array_like (this);
}
+/* See gdbtypes.h. */
+
+bool
+type::is_void ()
+{
+ return check_typedef (this)->code () == TYPE_CODE_VOID;
+}
+
+const std::string
+type::printable_name () const
+{
+ string_file thetype;
+
+ current_language->print_type (const_cast<type*>(this), "",
+ &thetype, -1, 0,
+ &type_print_raw_options);
+
+ return thetype.release ();
+}
\f
static const registry<gdbarch>::key<struct builtin_type> gdbtypes_data;
diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h
index ba216c6db54..b736d366fda 100644
--- a/gdb/gdbtypes.h
+++ b/gdb/gdbtypes.h
@@ -1054,6 +1054,9 @@ struct type
this->main_type->name = name;
}
+ /* Get the name of this type suitable for the user. */
+ const std::string printable_name () const;
+
/* Note that if thistype is a TYPEDEF type, you have to call check_typedef.
But check_typedef does set the TYPE_LENGTH of the TYPEDEF type,
so you only have to call check_typedef once. Since value::allocate
@@ -1526,6 +1529,9 @@ struct type
representations of arrays by the type's language. */
bool is_array_like ();
+ /* Return true if this type is "void". Follows typedefs. */
+ bool is_void ();
+
/* Return the language that this type came from. */
enum language language () const
{ return main_type->m_lang; }
diff --git a/gdb/python/py-type.c b/gdb/python/py-type.c
index c546aa7536a..3d6db708f8d 100644
--- a/gdb/python/py-type.c
+++ b/gdb/python/py-type.c
@@ -768,6 +768,135 @@ typy_unqualified (PyObject *self, PyObject *args)
return type_to_type_object (type);
}
+/* Return a function type. */
+
+static PyObject *
+typy_function (PyObject *self, PyObject *args, PyObject *kw)
+{
+ struct type *return_type = ((type_object *) self)->type;
+
+ /* Process arguments. We cannot simply use PyArg_ParseTupleAndKeywords
+ because this method take variable number of arguments. */
+ if (args == nullptr || !PyTuple_Check (args))
+ {
+ PyErr_Format (PyExc_ValueError,
+ _("Arguments is %s, not a tuple."),
+ Py_TYPE (args)->tp_name);
+ return nullptr;
+ }
+ int nparam_types = PyTuple_Size (args);
+ if (nparam_types == -1)
+ {
+ /* Can this really happen? At this point we _know_ that
+ ARGS is a tuple (or subclass). */
+ PyErr_Format (PyExc_ValueError,
+ _("Failed retrieve number of parameters."));
+ return nullptr;
+ }
+ std::vector<struct type *> param_types (nparam_types);
+ for (int i = 0; i < nparam_types; i++)
+ {
+ PyObject *param_type_obj = PySequence_GetItem (args, i);
+
+ if (param_type_obj == nullptr)
+ {
+ PyErr_Format (PyExc_ValueError,
+ _("Failed to retrieve parameter at index %d."), i);
+ return nullptr;
+ }
+ else
+ {
+ param_types[i] = type_object_to_type (param_type_obj);
+ if (param_types[i] == nullptr)
+ {
+ PyErr_Format (PyExc_TypeError,
+ _("Argument at index %d is %s, not a gdb.Type "
+ "object."),
+ i, Py_TYPE (param_type_obj)->tp_name);
+ return nullptr;
+ }
+ if (param_types[i]->is_void ())
+ {
+ PyErr_Format (PyExc_ValueError,
+ _("Argument at index %d is a void type but "
+ "void as parameter type is not allowed."),
+ i);
+ return nullptr;
+ }
+ }
+ }
+
+ if (kw != nullptr)
+ {
+ if (!PyDict_Check (kw))
+ {
+ PyErr_Format (PyExc_ValueError,
+ _("Arguments is %s, not a dict."),
+ Py_TYPE (args)->tp_name);
+ return nullptr;
+ }
+
+ PyObject *key, *value;
+ Py_ssize_t pos = 0;
+ while (PyDict_Next (kw, &pos, &key, &value))
+ {
+ if (!PyUnicode_Check (key))
+ {
+ PyErr_Format (PyExc_ValueError,
+ _("Keyword argument key is %s, not a string."),
+ Py_TYPE (key)->tp_name);
+ return nullptr;
+ }
+ if (PyUnicode_CompareWithASCIIString (key, "varargs") != 0)
+ {
+ PyErr_Format (PyExc_ValueError,
+ _("Invalid keyword argument \"%U\"."),
+ key);
+ return nullptr;
+ }
+ if (!PyBool_Check (value))
+ {
+ PyErr_Format (PyExc_ValueError,
+ _("Value of \"varargs\" argument is \"%s\", "
+ "not a bool."),
+ Py_TYPE (value)->tp_name);
+ return nullptr;
+ }
+ if (value == Py_True)
+ {
+ param_types.push_back (nullptr);
+ }
+ }
+ }
+
+ /* Copy all objfile-owned types to arch. This way user does not
+ need to care about mixing types from different objfiles and
+ architectures and we do not need to expose this implementation
+ detail to the user. */
+
+ copied_types_hash_t copied_types;
+ return_type = copy_type_recursive (return_type, copied_types);
+ for (int i = 0; i < param_types.size (); i++)
+ {
+ if (param_types[i] != nullptr)
+ param_types[i] = copy_type_recursive (param_types[i], copied_types);
+ }
+
+ struct type *function_type = nullptr;
+ try
+ {
+ function_type = lookup_function_type_with_arguments
+ (return_type, param_types.size (), param_types.data ());
+ function_type->set_is_prototyped (true);
+ }
+ catch (const gdb_exception &except)
+ {
+ return gdbpy_handle_gdb_exception (nullptr, except);
+ }
+
+ return type_to_type_object (function_type);
+}
+
/* Return the size of the type represented by SELF, in bytes. */
static PyObject *
typy_get_sizeof (PyObject *self, void *closure)
@@ -1630,6 +1759,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", (PyCFunction) typy_function, METH_VARARGS | METH_KEYWORDS,
+ "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 0bc4556c653..876ef474a32 100644
--- a/gdb/testsuite/gdb.python/py-type.exp
+++ b/gdb/testsuite/gdb.python/py-type.exp
@@ -378,6 +378,42 @@ 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_no_output "python uu_t = gdb.parse_and_eval ('uu').type"
+
+ gdb_test "python print(repr(int_t.function()))" \
+ "<gdb.Type code=TYPE_CODE_FUNC name=int \\(void\\)>"
+
+ 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, varargs=True)))" \
+ "<gdb.Type code=TYPE_CODE_FUNC name=int \\(int, ...\\)>"
+
+ gdb_test "python print(repr(int_t.function(varargs=True)))" \
+ "<gdb.Type code=TYPE_CODE_FUNC name=int \\(void\\)>"
+
+ gdb_test "python print(repr(int_t.function(varargs=False)))" \
+ "<gdb.Type code=TYPE_CODE_FUNC name=int \\(void\\)>"
+
+ gdb_test "python print(repr(int_t.function(123)))" \
+ "TypeError.*:.*"
+
+ gdb_test "python print(repr(int_t.function(gdb.lookup_type('void'))))" \
+ "ValueError.*:.*"
+
+ gdb_test "python print(repr(int_t.function(int_t, gdb.lookup_type('void'))))" \
+ "ValueError.*:.*"
+
+ gdb_test "python print(repr(int_t.function(int_t, varargs=\[1,2,3\])))" \
+ "ValueError.*:.*"
+
+ gdb_test "python print(repr(int_t.function(int_t, kw=False)))" \
+ "ValueError.*:.*"
+
+ gdb_test "python print(repr(int_t.function(gdb.lookup_type('void'), varargs=True)))" \
+ "ValueError.*:.*"
+
set sint [get_sizeof int 0]
gdb_test "python print(gdb.parse_and_eval('aligncheck').type.alignof)" \
$sint
--
2.47.2
next prev parent reply other threads:[~2025-06-23 16:13 UTC|newest]
Thread overview: 31+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-06-23 16:09 [RFC v5 00/19] Add Python "JIT" API Jan Vrany
2025-06-23 16:09 ` [RFC v5 01/18] gdb: introduce expand_symtabs_maybe_overlapping Jan Vrany
2025-06-24 15:22 ` Tom Tromey
2025-06-26 15:05 ` Jan Vraný
2025-06-23 16:09 ` [RFC v5 02/18] gdb: introduce compunit_symtab::maybe_contains Jan Vrany
2025-06-23 16:09 ` [RFC v5 03/18] gdb: update is_addr_in_objfile to support "dynamic" objfiles Jan Vrany
2025-06-23 16:09 ` [RFC v5 04/18] gdb: introduce new function create_function_type Jan Vrany
2025-06-24 15:29 ` Tom Tromey
2025-06-26 11:12 ` Jan Vraný
2025-06-27 14:21 ` Tom Tromey
2025-06-27 14:30 ` Jan Vraný
2025-06-23 16:10 ` Jan Vrany [this message]
2025-06-24 16:11 ` [RFC v5 05/18] gdb/python: add function () method to gdb.Type object Tom Tromey
2025-06-26 11:13 ` Jan Vraný
2025-06-23 16:10 ` [RFC v5 06/18] gdb: use std::vector<> to hold on blocks in struct blockvector Jan Vrany
2025-06-23 16:10 ` [RFC v5 07/18] gdb/python: add gdb.Compunit Jan Vrany
2025-06-23 16:10 ` [RFC v5 08/18] gdb/python: allow instantiation of gdb.Objfile from Python Jan Vrany
2025-06-23 16:10 ` [RFC v5 09/18] gdb/python: add unlink () method to gdb.Objfile object Jan Vrany
2025-06-23 16:10 ` [RFC v5 10/18] gdb/python: allow instantiation of gdb.Compunit from Python Jan Vrany
2025-06-23 16:10 ` [RFC v5 11/18] gdb/python: allow instantiation of gdb.Symtab " Jan Vrany
2025-06-23 16:10 ` [RFC v5 12/18] gdb/python: allow instantiation of gdb.Block " Jan Vrany
2025-06-23 16:10 ` [RFC v5 13/18] gdb/python: allow instantiation of gdb.Symbol " Jan Vrany
2025-06-23 16:10 ` [RFC v5 14/18] gdb/python: add add_symbol () method to gdb.Block Jan Vrany
2025-08-29 14:10 ` Andrew Burgess
2025-08-29 14:14 ` Andrew Burgess
2025-06-23 16:10 ` [RFC v5 15/18] gdb/python: add more attributes to gdb.LinetableEntry objects Jan Vrany
2025-08-29 14:00 ` Andrew Burgess
2025-09-02 11:03 ` Jan Vraný
2025-06-23 16:10 ` [RFC v5 16/18] gdb/python: allow instantiation of gdb.LineTableEntry objects Jan Vrany
2025-06-23 16:10 ` [RFC v5 17/18] gdb/python: allow instantiation of gdb.LineTable objects Jan Vrany
2025-06-23 16:10 ` [RFC v5 18/18] gdb/python: add section in documentation on implementing JIT interface Jan Vrany
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250623161013.650814-6-jan.vrany@labware.com \
--to=jan.vrany@labware.com \
--cc=eliz@gnu.org \
--cc=gdb-patches@sourceware.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox