Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: "Jan Vraný" <Jan.Vrany@labware.com>
To: "gdb-patches@sourceware.org" <gdb-patches@sourceware.org>
Cc: "tom@tromey.com" <tom@tromey.com>
Subject: Re: [RFC v5 05/18] gdb/python: add function () method to gdb.Type object
Date: Thu, 26 Jun 2025 11:13:08 +0000	[thread overview]
Message-ID: <6d860753bc262423186ccbc434d4ede19690f096.camel@labware.com> (raw)
In-Reply-To: <87y0thxgop.fsf@tromey.com>

On Tue, 2025-06-24 at 10:11 -0600, Tom Tromey wrote:
> > > > > > "Jan" == Jan Vrany <jan.vrany@labware.com> writes:
> 
> Jan> +/* See gdbtypes.h.  */
> Jan> +
> Jan> +bool
> Jan> +type::is_void ()
> Jan> +{
> Jan> +  return check_typedef (this)->code () == TYPE_CODE_VOID;
> Jan> +}
> 
> This should be moved to the earlier patch that calls it.
> 

I did it the other way around since this commit actually makes
use of it in typy_function(). 

> Jan> +const std::string
> Jan> +type::printable_name () const
> Jan> +{
> 
> This isn't called in this patch but I don't think it really matters
> as
> long as it's used eventually.

The reason for this was to provide nicer error message in earlier 
versions of this patch but it is not used now that this copies objfile-
owned types to the arch. So I'll drop it for now. 

> 
> Jan> +  string_file thetype;
> Jan> +
> Jan> +  current_language->print_type (const_cast<type*>(this), "",
> 
> Space before "(this)"
> 
> Jan> +   param_types[i] = type_object_to_type (param_type_obj);
> Jan> +   if (param_types[i] == nullptr)
> Jan> +     {
> Jan> +       PyErr_Format (PyExc_TypeError,
> Jan> +     _("Argument at index %d is %s, not a gdb.Type "
> Jan> +       "object."),
> Jan> +     i, Py_TYPE (param_type_obj)->tp_name);
> Jan> +       return nullptr;
> 
> It's kind of lame that we don't have a type_object_to_type_checked
> variant.

I'm not sure what do you mean here, can you please elaborate a bit?

> 
> Jan> +   if (param_types[i]->is_void ())
> 
> Can check_typedef throw an exception?  If so this must be wrapped in
> a
> try/catch.  That's a good practice for all calls into gdb anyway,
> because things like this may change over time.
> 
> Jan> +  if (kw != nullptr)
> Jan> +    {
> Jan> +      if (!PyDict_Check (kw))
> Jan> + {
> Jan> +   PyErr_Format (PyExc_ValueError,
> Jan> + _("Arguments is %s, not a dict."),
> Jan> + Py_TYPE (args)->tp_name);
> Jan> +   return nullptr;
> Jan> + }
> Jan> +
> 
> This code should probably call PyArg_ValidateKeywordArguments.
> I don't know whether that includes the PyDict_Check on its own.

Looking at the python source code, it does PyDict_Check().
I changed the code to make it little, makes code a little shorter.

> 
> Jan> +  /* Copy all objfile-owned types to arch. This way user does
> not
> Jan> +     need to care about mixing types from different objfiles
> and
> Jan> +     architectures and we do not need to expose this
> implementation
> Jan> +     detail to the user.  */
> Jan> +
> Jan> +  copied_types_hash_t copied_types;
> Jan> +  return_type = copy_type_recursive (return_type,
> copied_types);
> 
> This whole block should probably be wrapped in a try/catch.
> 
> Jan> + function_type = lookup_function_type_with_arguments
> Jan> +        (return_type, param_types.size (), param_types.data
> ());
> 
> May be mildly nicer to use an array_view here.
> 

Yes. This is pre-existing function so I did not changed that. Looking
at users, all use std::vector to store types so I can change the
function to just take a vector. 

> Jan> +  gdb_test "python print(repr(int_t.function(varargs=True)))" \
> Jan> +      "<gdb.Type code=TYPE_CODE_FUNC name=int \\(void\\)>"
> Jan> +
> Jan> +  gdb_test "python print(repr(int_t.function(varargs=False)))"
> \
> Jan> +      "<gdb.Type code=TYPE_CODE_FUNC name=int \\(void\\)>"
> 
> Seems weird that these print the same thing.

Yes, this is perhaps bug in type printing. See #32452 

https://sourceware.org/bugzilla/show_bug.cgi?id=32452

and also my earlier conversation with Andrew

https://inbox.sourceware.org/gdb-patches/bae30cf2e19b236f55ae38cf7dcf4f17dad2c4be.camel@labware.com/



Below you may find updated patch. I'll resend the whole series once
more updates/comments accumulate. Thanks a lot! Jan

-- >8 --
From fe166a218161c07e0aa9cec9059a47280efff07e Mon Sep 17 00:00:00 2001
From: Jan Vrany <jan.vrany@labware.com>
Date: Wed, 25 Jun 2025 19:50:13 +0100
Subject: [PATCH 06/19] gdb/python: add function () method to gdb.Type
object

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.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
---
 gdb/NEWS                             |   3 +
 gdb/doc/python.texi                  |  11 +++
 gdb/gdbtypes.c                       |  11 ++-
 gdb/gdbtypes.h                       |   3 +
 gdb/python/py-type.c                 | 127 +++++++++++++++++++++++++++
 gdb/testsuite/gdb.python/py-type.exp |  36 ++++++++
 6 files changed, 189 insertions(+), 2 deletions(-)

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 ac2dc52f555..964a0360257 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
@@ -581,8 +582,7 @@ create_function_type (type_allocator &alloc,
 --nparams;
 fn->set_has_varargs (true);
 }
-      else if (check_typedef (param_types[nparams - 1])->code ()
- == TYPE_CODE_VOID)
+      else if ((param_types[nparams - 1])->is_void ())
 {
 --nparams;
 /* Caller should have ensured this. */
@@ -5976,6 +5976,13 @@ 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;
+}
 \f
 
 static const registry<gdbarch>::key<struct builtin_type>
gdbtypes_data;
diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h
index ba216c6db54..327c29bd3ff 100644
--- a/gdb/gdbtypes.h
+++ b/gdb/gdbtypes.h
@@ -1526,6 +1526,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..c2f7def5dfa 100644
--- a/gdb/python/py-type.c
+++ b/gdb/python/py-type.c
@@ -768,6 +768,130 @@ 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;
+ }
+ try
+ {
+ 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;
+ }
+ }
+ catch (const gdb_exception &except)
+ {
+ return gdbpy_handle_gdb_exception (nullptr, except);
+ }
+ }
+    }
+
+  if (kw != nullptr)
+    {
+      if (!PyArg_ValidateKeywordArguments (kw))
+ return nullptr;
+
+      PyObject *key, *value;
+      Py_ssize_t pos = 0;
+      while (PyDict_Next (kw, &pos, &key, &value))
+ {
+ 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);
+ }
+ }
+    }
+
+  struct type *function_type = nullptr;
+  try
+    {
+      /* 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);
+ }
+
+      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 +1754,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


  reply	other threads:[~2025-06-26 11:14 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 ` [RFC v5 05/18] gdb/python: add function () method to gdb.Type object Jan Vrany
2025-06-24 16:11   ` Tom Tromey
2025-06-26 11:13     ` Jan Vraný [this message]
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=6d860753bc262423186ccbc434d4ede19690f096.camel@labware.com \
    --to=jan.vrany@labware.com \
    --cc=gdb-patches@sourceware.org \
    --cc=tom@tromey.com \
    /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