* [PATCH v2 0/6] gdb: minor fixes for Python limited C API support
@ 2026-01-27 17:02 Matthieu Longo
2026-01-27 17:02 ` [PATCH v2 1/6] Python limited API: migrate Py_CompileStringExFlags and PyRun_SimpleString Matthieu Longo
` (5 more replies)
0 siblings, 6 replies; 20+ messages in thread
From: Matthieu Longo @ 2026-01-27 17:02 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey, Matthieu Longo
This patch series fixes a set of minor issues encountered while enabling the Python limited C API in GDB, and starts preparing the ground for migrating the existing C extension types from static types to heap-allocated ones, by removing the dependency on tp_dictoffset, which is unavailable when using the limited API. Those fixes were accumulated during the migration work, and are intentionally split out from an upcoming larger patch series. The first three patches in the series are small, and self-contained fixes. The next three ones continue the work undertaken in the third one, and have a dependency on previous patches.
All changes were tested by building GDB against the limited API and unlimited API, and no regressions were observed in the testsuite.
# Revision 1 -> 2
Patch series v1: https://inbox.sourceware.org/gdb-patches/20260107165606.1719366-1-matthieu.longo@arm.com
Major changes since the last revision:
- addressed minor comments on patch 1 and 2.
- patch 3: inherits from PyObject.
New:
- patch 4: inherits from PyObject, but also had to provide a different approach to provide a dict because offsetof does not work anymore with inheritance. Anyway, this had to be fixed so two birds with one stone.
- patch 5: enforces that the template type is a subclass of PyObject in gdbpy_ref_policy.
- patch 6: finish migrating all the remaing declarations.
Regards,
Matthieu
Matthieu Longo (6):
Python limited API: migrate Py_CompileStringExFlags and PyRun_SimpleString
Python limited API: migrate PyImport_ExtendInittab
gdbpy_registry: cast C extension type object to PyObject * before Py_XINCREF
gdb: new setters and getters for __dict__, and attributes
gdb: cast all Python extension objects passed to gdbpy_ref_policy to PyObject*
gdb: make remaining Python extension objects inherit from PyObject
gdb/python/py-arch.c | 4 +-
gdb/python/py-block.c | 8 +--
gdb/python/py-breakpoint.c | 4 +-
gdb/python/py-cmd.c | 4 +-
gdb/python/py-color.c | 4 +-
gdb/python/py-connection.c | 4 +-
gdb/python/py-corefile.c | 26 +++-------
gdb/python/py-disasm.c | 16 ++----
gdb/python/py-event.c | 10 ++--
gdb/python/py-event.h | 8 +--
gdb/python/py-events.h | 4 +-
gdb/python/py-frame.c | 4 +-
gdb/python/py-gdb-readline.c | 5 +-
gdb/python/py-inferior.c | 29 ++---------
gdb/python/py-infthread.c | 10 ++--
gdb/python/py-instruction.c | 5 +-
gdb/python/py-lazy-string.c | 5 +-
gdb/python/py-linetable.c | 12 ++---
gdb/python/py-membuf.c | 5 +-
gdb/python/py-micmd.c | 4 +-
gdb/python/py-objfile.c | 18 +++----
gdb/python/py-param.c | 4 +-
gdb/python/py-prettyprint.c | 6 +--
gdb/python/py-progspace.c | 18 +++----
gdb/python/py-record-btrace.c | 5 +-
gdb/python/py-record.c | 4 +-
gdb/python/py-record.h | 8 +--
gdb/python/py-ref.h | 47 ++++++++++++++++-
gdb/python/py-registers.c | 20 +++-----
gdb/python/py-style.c | 4 +-
gdb/python/py-symbol.c | 4 +-
gdb/python/py-symtab.c | 8 +--
gdb/python/py-tui.c | 4 +-
gdb/python/py-type.c | 26 ++++------
gdb/python/py-unwind.c | 8 +--
gdb/python/py-utils.c | 95 +++++++++++++++++++++++++++++++----
gdb/python/py-value.c | 4 +-
gdb/python/python-internal.h | 47 ++++++++++++-----
gdb/python/python.c | 27 ++++------
39 files changed, 281 insertions(+), 247 deletions(-)
--
2.52.0
^ permalink raw reply [flat|nested] 20+ messages in thread* [PATCH v2 1/6] Python limited API: migrate Py_CompileStringExFlags and PyRun_SimpleString 2026-01-27 17:02 [PATCH v2 0/6] gdb: minor fixes for Python limited C API support Matthieu Longo @ 2026-01-27 17:02 ` Matthieu Longo 2026-01-27 17:54 ` Tom Tromey 2026-01-27 17:02 ` [PATCH v2 2/6] Python limited API: migrate PyImport_ExtendInittab Matthieu Longo ` (4 subsequent siblings) 5 siblings, 1 reply; 20+ messages in thread From: Matthieu Longo @ 2026-01-27 17:02 UTC (permalink / raw) To: gdb-patches; +Cc: Tom Tromey, Matthieu Longo This patch replaces Py_CompileStringExFlags () with its limited C API equivalent, Py_CompileString (). The eval_python_command () helper is now exposed through the private GDB Python API as a utility function. PyRun_SimpleString () is replaced with eval_python_command () to avoid code duplication. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=23830 --- gdb/python/py-gdb-readline.c | 5 +++-- gdb/python/python-internal.h | 3 +++ gdb/python/python.c | 17 ++++++++--------- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/gdb/python/py-gdb-readline.c b/gdb/python/py-gdb-readline.c index f0fe967f073..e8e2c23547c 100644 --- a/gdb/python/py-gdb-readline.c +++ b/gdb/python/py-gdb-readline.c @@ -102,7 +102,7 @@ gdbpy_initialize_gdb_readline () The third default finder is the one that will load readline, so the custom finder to disable the import of readline in GDB has to be placed before this third default finder. */ - if (PyRun_SimpleString ("\ + const char *code = "\ import sys\n\ from importlib.abc import MetaPathFinder\n\ \n\ @@ -113,7 +113,8 @@ class GdbRemoveReadlineFinder(MetaPathFinder):\n\ raise ImportError(\"readline module disabled under GDB\")\n\ \n\ sys.meta_path.insert(2, GdbRemoveReadlineFinder())\n\ -") == 0) +"; + if (eval_python_command (code, Py_file_input) == 0) PyOS_ReadlineFunctionPointer = gdbpy_readline_wrapper; return 0; diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h index 85c76779a49..65d2eee38ed 100644 --- a/gdb/python/python-internal.h +++ b/gdb/python/python-internal.h @@ -1319,4 +1319,7 @@ class gdbpy_memoizing_registry_storage gdb::unordered_map<val_type *, obj_type *> m_objects; }; +extern int eval_python_command (const char *command, int start_symbol, + const char *filename = nullptr); + #endif /* GDB_PYTHON_PYTHON_INTERNAL_H */ diff --git a/gdb/python/python.c b/gdb/python/python.c index 87fce272c42..989add70d1a 100644 --- a/gdb/python/python.c +++ b/gdb/python/python.c @@ -299,9 +299,9 @@ gdbpy_check_quit_flag (const struct extension_language_defn *extlang) NULL means that this is evaluating a string, not the contents of a file. */ -static int +int eval_python_command (const char *command, int start_symbol, - const char *filename = nullptr) + const char *filename) { PyObject *m, *d; @@ -340,13 +340,12 @@ eval_python_command (const char *command, int start_symbol, } } - /* Use this API because it is in Python 3.2. */ - gdbpy_ref<> code (Py_CompileStringExFlags (command, - filename == nullptr - ? "<string>" - : filename, - start_symbol, - nullptr, -1)); + /* Use this API because it is available with the Python limited API. */ + gdbpy_ref<> code (Py_CompileString (command, + filename == nullptr + ? "<string>" + : filename, + start_symbol)); int result = -1; if (code != nullptr) -- 2.52.0 ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2 1/6] Python limited API: migrate Py_CompileStringExFlags and PyRun_SimpleString 2026-01-27 17:02 ` [PATCH v2 1/6] Python limited API: migrate Py_CompileStringExFlags and PyRun_SimpleString Matthieu Longo @ 2026-01-27 17:54 ` Tom Tromey 0 siblings, 0 replies; 20+ messages in thread From: Tom Tromey @ 2026-01-27 17:54 UTC (permalink / raw) To: Matthieu Longo; +Cc: gdb-patches, Tom Tromey >>>>> "Matthieu" == Matthieu Longo <matthieu.longo@arm.com> writes: Matthieu> This patch replaces Py_CompileStringExFlags () with its limited C API Matthieu> equivalent, Py_CompileString (). The eval_python_command () helper is Matthieu> now exposed through the private GDB Python API as a utility function. Matthieu> PyRun_SimpleString () is replaced with eval_python_command () to avoid Matthieu> code duplication. Matthieu> Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=23830 Looks good, thank you. Approved-By: Tom Tromey <tom@tromey.com> Tom ^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH v2 2/6] Python limited API: migrate PyImport_ExtendInittab 2026-01-27 17:02 [PATCH v2 0/6] gdb: minor fixes for Python limited C API support Matthieu Longo 2026-01-27 17:02 ` [PATCH v2 1/6] Python limited API: migrate Py_CompileStringExFlags and PyRun_SimpleString Matthieu Longo @ 2026-01-27 17:02 ` Matthieu Longo 2026-01-27 17:54 ` Tom Tromey 2026-01-27 17:02 ` [PATCH v2 3/6] gdbpy_registry: cast C extension type object to PyObject * before Py_XINCREF Matthieu Longo ` (3 subsequent siblings) 5 siblings, 1 reply; 20+ messages in thread From: Matthieu Longo @ 2026-01-27 17:02 UTC (permalink / raw) To: gdb-patches; +Cc: Tom Tromey, Matthieu Longo This patch replaces PyImport_ExtendInittab () with its limited C API equivalent, PyImport_AppendInittab (), a convenience wrapper around PyImport_ExtendInittab (). Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=23830 --- gdb/python/python.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/gdb/python/python.c b/gdb/python/python.c index 989add70d1a..79b5d24d1cf 100644 --- a/gdb/python/python.c +++ b/gdb/python/python.c @@ -2590,14 +2590,8 @@ do_start_initialization () { /* Define all internal modules. These are all imported (and thus created) during initialization. */ - struct _inittab mods[] = - { - { "_gdb", init__gdb_module }, - { "_gdbevents", gdbpy_events_mod_func }, - { nullptr, nullptr } - }; - - if (PyImport_ExtendInittab (mods) < 0) + if (PyImport_AppendInittab ("_gdb", init__gdb_module) < 0 + || PyImport_AppendInittab ("_gdbevents", gdbpy_events_mod_func) < 0) return false; if (!py_initialize ()) -- 2.52.0 ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2 2/6] Python limited API: migrate PyImport_ExtendInittab 2026-01-27 17:02 ` [PATCH v2 2/6] Python limited API: migrate PyImport_ExtendInittab Matthieu Longo @ 2026-01-27 17:54 ` Tom Tromey 0 siblings, 0 replies; 20+ messages in thread From: Tom Tromey @ 2026-01-27 17:54 UTC (permalink / raw) To: Matthieu Longo; +Cc: gdb-patches, Tom Tromey >>>>> "Matthieu" == Matthieu Longo <matthieu.longo@arm.com> writes: Matthieu> This patch replaces PyImport_ExtendInittab () with its limited C Matthieu> API equivalent, PyImport_AppendInittab (), a convenience wrapper Matthieu> around PyImport_ExtendInittab (). Matthieu> Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=23830 Thanks. Approved-By: Tom Tromey <tom@tromey.com> Tom ^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH v2 3/6] gdbpy_registry: cast C extension type object to PyObject * before Py_XINCREF 2026-01-27 17:02 [PATCH v2 0/6] gdb: minor fixes for Python limited C API support Matthieu Longo 2026-01-27 17:02 ` [PATCH v2 1/6] Python limited API: migrate Py_CompileStringExFlags and PyRun_SimpleString Matthieu Longo 2026-01-27 17:02 ` [PATCH v2 2/6] Python limited API: migrate PyImport_ExtendInittab Matthieu Longo @ 2026-01-27 17:02 ` Matthieu Longo 2026-01-27 18:01 ` Tom Tromey 2026-01-27 18:29 ` Tom Tromey 2026-01-27 17:02 ` [PATCH v2 4/6] gdb: new setters and getters for __dict__, and attributes Matthieu Longo ` (2 subsequent siblings) 5 siblings, 2 replies; 20+ messages in thread From: Matthieu Longo @ 2026-01-27 17:02 UTC (permalink / raw) To: gdb-patches; +Cc: Tom Tromey, Matthieu Longo When enabling the Python limited API, pointers to Python C extension objects can no longer be implicitly converted to 'PyObject *' by the compiler. The lookup() method of gbdpy_registry returns a new reference to the type object of the looked-up entry. It does so by calling Py_XINCREF() to increment the reference counter of the returned type object. The template parameter obj_type corresponds to the type of C extension object type. With the Python limited API enabled, obj_type can no longer be implicitly converted to 'PyObject *' when passed to Py_XINCREF(). This patch fixes the resulting compilation issue by adding an explicit static_cast to 'PyObject *' before passing the value to Py_XINCREF(). As a side effect, this cast enforces, at compile time, that the template type 'Storage::obj_type' passed to gdbpy_registry is a subclass of PyObject. To provide a clearer diagnostic when an incorrect type is used, a static_assert is added to gdbpy_registry, avoiding obscure errors originating from the static_cast. Finally, the relevant C extension types passed to gdbpy_registry are updated to inherit publicly from PyObject. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=23830 --- gdb/python/py-symbol.c | 4 ++-- gdb/python/py-symtab.c | 8 ++++---- gdb/python/py-type.c | 3 +-- gdb/python/python-internal.h | 5 ++++- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/gdb/python/py-symbol.c b/gdb/python/py-symbol.c index bd4023fa31a..29389739841 100644 --- a/gdb/python/py-symbol.c +++ b/gdb/python/py-symbol.c @@ -25,8 +25,8 @@ #include "objfiles.h" #include "symfile.h" -struct symbol_object { - PyObject_HEAD +struct symbol_object: public PyObject +{ /* The GDB symbol structure this object is wrapping. */ struct symbol *symbol; }; diff --git a/gdb/python/py-symtab.c b/gdb/python/py-symtab.c index 9c093e70fc8..04d2d7d0289 100644 --- a/gdb/python/py-symtab.c +++ b/gdb/python/py-symtab.c @@ -24,8 +24,8 @@ #include "objfiles.h" #include "block.h" -struct symtab_object { - PyObject_HEAD +struct symtab_object: public PyObject +{ /* The GDB Symbol table structure. */ struct symtab *symtab; }; @@ -47,8 +47,8 @@ static const gdbpy_registry<gdbpy_memoizing_registry_storage<symtab_object, } \ } while (0) -struct sal_object { - PyObject_HEAD +struct sal_object: public PyObject +{ /* The GDB Symbol table and line structure. */ struct symtab_and_line *sal; /* A Symtab and line object is associated with an objfile, so keep diff --git a/gdb/python/py-type.c b/gdb/python/py-type.c index 46004b93acd..40adbf0bb7d 100644 --- a/gdb/python/py-type.c +++ b/gdb/python/py-type.c @@ -28,9 +28,8 @@ #include "typeprint.h" #include "ada-lang.h" -struct type_object +struct type_object: public PyObject { - PyObject_HEAD struct type *type; }; diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h index 65d2eee38ed..95619bf775e 100644 --- a/gdb/python/python-internal.h +++ b/gdb/python/python-internal.h @@ -1157,6 +1157,9 @@ class gdbpy_registry using obj_type = typename Storage::obj_type; using val_type = typename Storage::val_type; + static_assert(std::is_base_of<PyObject, obj_type>::value, + "obj_type must be a subclass of PyObject"); + /* Register Python object OBJ as being "owned" by OWNER. When OWNER is about to be freed, OBJ will be invalidated. */ template <typename O> @@ -1180,7 +1183,7 @@ class gdbpy_registry obj_type *lookup (O *owner, val_type *val) const { obj_type *obj = get_storage (owner)->lookup (val); - Py_XINCREF (obj); + Py_XINCREF (static_cast<PyObject *> (obj)); return obj; } -- 2.52.0 ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2 3/6] gdbpy_registry: cast C extension type object to PyObject * before Py_XINCREF 2026-01-27 17:02 ` [PATCH v2 3/6] gdbpy_registry: cast C extension type object to PyObject * before Py_XINCREF Matthieu Longo @ 2026-01-27 18:01 ` Tom Tromey 2026-01-27 18:29 ` Tom Tromey 1 sibling, 0 replies; 20+ messages in thread From: Tom Tromey @ 2026-01-27 18:01 UTC (permalink / raw) To: Matthieu Longo; +Cc: gdb-patches, Tom Tromey >>>>> "Matthieu" == Matthieu Longo <matthieu.longo@arm.com> writes: Matthieu> This patch fixes the resulting compilation issue by adding an explicit Matthieu> static_cast to 'PyObject *' before passing the value to Py_XINCREF(). Matthieu> As a side effect, this cast enforces, at compile time, that the template Matthieu> type 'Storage::obj_type' passed to gdbpy_registry is a subclass of Matthieu> PyObject. To provide a clearer diagnostic when an incorrect type is used, Matthieu> a static_assert is added to gdbpy_registry, avoiding obscure errors Matthieu> originating from the static_cast. Finally, the relevant C extension types Matthieu> passed to gdbpy_registry are updated to inherit publicly from PyObject. Thanks, this looks nice. I suspect the static_cast isn't needed, but I think it also isn't harmful, especially with the static assert in place. Approved-By: Tom Tromey <tom@tromey.com> Tom ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2 3/6] gdbpy_registry: cast C extension type object to PyObject * before Py_XINCREF 2026-01-27 17:02 ` [PATCH v2 3/6] gdbpy_registry: cast C extension type object to PyObject * before Py_XINCREF Matthieu Longo 2026-01-27 18:01 ` Tom Tromey @ 2026-01-27 18:29 ` Tom Tromey 2026-01-28 11:58 ` Matthieu Longo 1 sibling, 1 reply; 20+ messages in thread From: Tom Tromey @ 2026-01-27 18:29 UTC (permalink / raw) To: Matthieu Longo; +Cc: gdb-patches, Tom Tromey >>>>> "Matthieu" == Matthieu Longo <matthieu.longo@arm.com> writes: Matthieu> +struct symbol_object: public PyObject First time around I didn't notice the missing space. This should be fixed throughout the patch. Tom ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2 3/6] gdbpy_registry: cast C extension type object to PyObject * before Py_XINCREF 2026-01-27 18:29 ` Tom Tromey @ 2026-01-28 11:58 ` Matthieu Longo 0 siblings, 0 replies; 20+ messages in thread From: Matthieu Longo @ 2026-01-28 11:58 UTC (permalink / raw) To: Tom Tromey; +Cc: gdb-patches On 27/01/2026 18:29, Tom Tromey wrote: >>>>>> "Matthieu" == Matthieu Longo <matthieu.longo@arm.com> writes: > > Matthieu> +struct symbol_object: public PyObject > > First time around I didn't notice the missing space. > This should be fixed throughout the patch. > > Tom Fixed. Matthieu ^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH v2 4/6] gdb: new setters and getters for __dict__, and attributes 2026-01-27 17:02 [PATCH v2 0/6] gdb: minor fixes for Python limited C API support Matthieu Longo ` (2 preceding siblings ...) 2026-01-27 17:02 ` [PATCH v2 3/6] gdbpy_registry: cast C extension type object to PyObject * before Py_XINCREF Matthieu Longo @ 2026-01-27 17:02 ` Matthieu Longo 2026-01-27 19:06 ` Tom Tromey 2026-01-27 17:02 ` [PATCH v2 5/6] gdb: cast all Python extension objects passed to gdbpy_ref_policy to PyObject* Matthieu Longo 2026-01-27 17:02 ` [PATCH v2 6/6] gdb: make remaining Python extension objects inherit from PyObject Matthieu Longo 5 siblings, 1 reply; 20+ messages in thread From: Matthieu Longo @ 2026-01-27 17:02 UTC (permalink / raw) To: gdb-patches; +Cc: Tom Tromey, Matthieu Longo GDB is currently using the Python unlimited API. Migrating the codebase to the Python limited API would have for benefit to make a GDB build artifacts compatible with older and newer versions of Python that they were built with. This patch prepares the ground for migrating the existing C extension types from static types to heap-allocated ones, by removing the dependency on tp_dictoffset, which is unavailable when using the limited API. One of the most common incompatibilities in the current static type declarations is the tp_dictoffset slot, which specifies the dictionary offset within the instance structure. Historically, the unlimited API has provided two approaches to supply a dictionary for __dict__: * A managed dictionary. Setting Py_TPFLAGS_MANAGED_DICT in tp_flags indicates that the instances of the type have a __dict__ attribute, and that the dictionary is managed by Python. According to the Python documentation, this is the recommended approach. However, this flag was introduced in 3.12, together with PyObject_VisitManagedDict() and PyObject_ClearManagedDict(), neither of which is part of the limited API (at least for now). As a result, this recommended approach is not viable in the context of the limited API. * An instance dictionary, for which the offset in the instance is provided via tp_dictoffset. According to the Python documentation, this "tp slot" is on the deprecation path, and Py_TPFLAGS_MANAGED_DICT should be used instead. Given the age of the GDB codebase and the requirement to support older Python versions (>= 3.4), no need to argue that today, the implementation of __dict__ relies on tp_dictoffset. However, in the context of the limited API, PyType_Slot does not provide a Py_tp_dictoffset member, so another approach is needed to provide __dict__ to instances of C extension types. Given the constraints of the limited API, the proposed solution consists in providing a dictionary through a common base class, gdbpy__dict__wrapper. This helper class owns a dictionary member corresponding to __dict__, and any C extension type requiring a __dict__ must inherit from it. Since extension object must also be convertible to PyObject, this wrapper class publicly inherits from PyObject as well. Access to the dictionary is provided via a custom getter defined in a PyGetSetDef, similarily to what was previously done with gdb_py_generic_dict(). Because __dict__ participates in attribute look-up, and since this dictionary is neither managed by Python nor exposed via tp_dictoffset, custom implementations of tp_getattro and tp_setattro are required to correctly redirect attribute look-ups to the dictionary. These custom implementations — equivalent to PyObject_GenericGetAttr() and PyObject_GenericSetAttr() — must be installed via tp_getattro / tp_setattro for static types, or Py_tp_getattro / Py_tp_setattro for heap-allocated types. - gdbpy__dict__wrapper: a base class for C extension objects that own a __dict__. - gdb_py_generic_dict_getter: a __dict__ getter for extension types derived from gdbpy__dict__wrapper. - gdb_py_generic_dict_setter: a __dict__ setter provided for completeness. It should not be used, as __dict__ should be read-only after the object initialization. - gdb_py_generic_getattro: equivalent of PyObject_GenericGetAttr, but fixes the look-up of __dict__. - gdb_py_generic_setattro: equivalent of PyObject_GenericSetAttr, but fixes the look-up of __dict__. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=23830 --- gdb/python/py-corefile.c | 18 +++---- gdb/python/py-event.c | 10 ++-- gdb/python/py-event.h | 8 +-- gdb/python/py-inferior.c | 29 ++--------- gdb/python/py-infthread.c | 10 ++-- gdb/python/py-objfile.c | 18 +++---- gdb/python/py-progspace.c | 18 +++---- gdb/python/py-ref.h | 40 +++++++++++++++ gdb/python/py-type.c | 19 +++----- gdb/python/py-utils.c | 95 +++++++++++++++++++++++++++++++----- gdb/python/python-internal.h | 35 +++++++++---- 11 files changed, 192 insertions(+), 108 deletions(-) diff --git a/gdb/python/py-corefile.c b/gdb/python/py-corefile.c index 6847722628f..4a6982231c8 100644 --- a/gdb/python/py-corefile.c +++ b/gdb/python/py-corefile.c @@ -26,20 +26,14 @@ /* A gdb.Corefile object. */ -struct corefile_object +struct corefile_object: public gdbpy__dict__wrapper { - PyObject_HEAD - /* The inferior this core file is attached to. This will be set to NULL when the inferior is deleted, or if a different core file is loaded for the inferior. When this is NULL the gdb.Corefile object is considered invalid.*/ struct inferior *inferior; - /* Dictionary holding user-added attributes. This is the __dict__ - attribute of the object. This is an owning reference. */ - PyObject *dict; - /* A Tuple of gdb.CorefileMappedFile objects. This tuple is only created the first time the user calls gdb.Corefile.mapped_files(), the result is cached here. If this pointer is not NULL then this is an owning @@ -511,8 +505,8 @@ GDBPY_INITIALIZE_FILE (gdbpy_initialize_corefile); static gdb_PyGetSetDef corefile_getset[] = { - { "__dict__", gdb_py_generic_dict, nullptr, - "The __dict__ for the gdb.Corefile.", &corefile_object_type }, + { "__dict__", gdb_py_generic_dict_getter, nullptr, + "The __dict__ for the gdb.Corefile.", nullptr }, { "filename", cfpy_get_filename, nullptr, "The filename of a valid Corefile object.", nullptr }, { nullptr } @@ -548,8 +542,8 @@ PyTypeObject corefile_object_type = 0, /*tp_hash */ 0, /*tp_call*/ 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ + gdb_py_generic_getattro, /*tp_getattro*/ + gdb_py_generic_setattro, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT, /*tp_flags*/ "GDB corefile object", /* tp_doc */ @@ -566,7 +560,7 @@ PyTypeObject corefile_object_type = 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ - offsetof (corefile_object, dict), /* tp_dictoffset */ + 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ diff --git a/gdb/python/py-event.c b/gdb/python/py-event.c index c985159a6f7..8fb21b7fdad 100644 --- a/gdb/python/py-event.c +++ b/gdb/python/py-event.c @@ -101,8 +101,8 @@ GDBPY_INITIALIZE_FILE (gdbpy_initialize_event); static gdb_PyGetSetDef event_object_getset[] = { - { "__dict__", gdb_py_generic_dict, NULL, - "The __dict__ for this event.", &event_object_type }, + { "__dict__", gdb_py_generic_dict_getter, NULL, + "The __dict__ for this event.", NULL }, { NULL } }; @@ -124,8 +124,8 @@ PyTypeObject event_object_type = 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ + gdb_py_generic_getattro, /* tp_getattro */ + gdb_py_generic_setattro, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ "GDB event object", /* tp_doc */ @@ -142,7 +142,7 @@ PyTypeObject event_object_type = 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ - offsetof (event_object, dict), /* tp_dictoffset */ + 0, /* tp_dictoffset */ 0, /* tp_init */ 0 /* tp_alloc */ }; diff --git a/gdb/python/py-event.h b/gdb/python/py-event.h index 6c81d64eb4f..d21ca1e2873 100644 --- a/gdb/python/py-event.h +++ b/gdb/python/py-event.h @@ -31,12 +31,8 @@ #include "py-event-types.def" #undef GDB_PY_DEFINE_EVENT_TYPE -struct event_object -{ - PyObject_HEAD - - PyObject *dict; -}; +struct event_object: public gdbpy__dict__wrapper +{}; extern int emit_continue_event (ptid_t ptid); extern int emit_exited_event (const LONGEST *exit_code, struct inferior *inf); diff --git a/gdb/python/py-inferior.c b/gdb/python/py-inferior.c index 8230f9d3943..69a7bfa3c37 100644 --- a/gdb/python/py-inferior.c +++ b/gdb/python/py-inferior.c @@ -32,25 +32,6 @@ #include "progspace-and-thread.h" #include "gdbsupport/unordered_map.h" -using thread_map_t - = gdb::unordered_map<thread_info *, gdbpy_ref<thread_object>>; - -struct inferior_object -{ - PyObject_HEAD - - /* The inferior we represent. */ - struct inferior *inferior; - - /* thread_object instances under this inferior. This owns a - reference to each object it contains. */ - thread_map_t *threads; - - /* Dictionary holding user-added attributes. - This is the __dict__ attribute of the object. */ - PyObject *dict; -}; - extern PyTypeObject inferior_object_type; /* Deleter to clean up when an inferior is removed. */ @@ -1061,8 +1042,8 @@ GDBPY_INITIALIZE_FILE (gdbpy_initialize_inferior); static gdb_PyGetSetDef inferior_object_getset[] = { - { "__dict__", gdb_py_generic_dict, nullptr, - "The __dict__ for this inferior.", &inferior_object_type }, + { "__dict__", gdb_py_generic_dict_getter, nullptr, + "The __dict__ for this inferior.", nullptr }, { "arguments", infpy_get_args, infpy_set_args, "Arguments to this program.", nullptr }, { "num", infpy_get_num, NULL, "ID of inferior, as assigned by GDB.", NULL }, @@ -1144,8 +1125,8 @@ PyTypeObject inferior_object_type = 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ + gdb_py_generic_getattro, /* tp_getattro */ + gdb_py_generic_setattro, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ "GDB inferior object", /* tp_doc */ @@ -1162,7 +1143,7 @@ PyTypeObject inferior_object_type = 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ - offsetof (inferior_object, dict), /* tp_dictoffset */ + 0, /* tp_dictoffset */ 0, /* tp_init */ 0 /* tp_alloc */ }; diff --git a/gdb/python/py-infthread.c b/gdb/python/py-infthread.c index e5d3222f9ae..d75742360d4 100644 --- a/gdb/python/py-infthread.c +++ b/gdb/python/py-infthread.c @@ -415,8 +415,8 @@ GDBPY_INITIALIZE_FILE (gdbpy_initialize_thread); static gdb_PyGetSetDef thread_object_getset[] = { - { "__dict__", gdb_py_generic_dict, nullptr, - "The __dict__ for this thread.", &thread_object_type }, + { "__dict__", gdb_py_generic_dict_getter, nullptr, + "The __dict__ for this thread.", nullptr }, { "name", thpy_get_name, thpy_set_name, "The name of the thread, as set by the user or the OS.", NULL }, { "details", thpy_get_details, NULL, @@ -479,8 +479,8 @@ PyTypeObject thread_object_type = 0, /*tp_hash */ 0, /*tp_call*/ 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ + gdb_py_generic_getattro, /*tp_getattro*/ + gdb_py_generic_setattro, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT, /*tp_flags*/ "GDB thread object", /* tp_doc */ @@ -497,7 +497,7 @@ PyTypeObject thread_object_type = 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ - offsetof (thread_object, dict), /* tp_dictoffset */ + 0, /* tp_dictoffset */ 0, /* tp_init */ 0 /* tp_alloc */ }; diff --git a/gdb/python/py-objfile.c b/gdb/python/py-objfile.c index 8cf365a27dc..802b85144ac 100644 --- a/gdb/python/py-objfile.c +++ b/gdb/python/py-objfile.c @@ -26,17 +26,11 @@ #include "python.h" #include "inferior.h" -struct objfile_object +struct objfile_object: public gdbpy__dict__wrapper { - PyObject_HEAD - /* The corresponding objfile. */ struct objfile *objfile; - /* Dictionary holding user-added attributes. - This is the __dict__ attribute of the object. */ - PyObject *dict; - /* The pretty-printer list of functions. */ PyObject *printers; @@ -739,8 +733,8 @@ Look up a static-linkage global symbol in this objfile and return it." }, static gdb_PyGetSetDef objfile_getset[] = { - { "__dict__", gdb_py_generic_dict, NULL, - "The __dict__ for this objfile.", &objfile_object_type }, + { "__dict__", gdb_py_generic_dict_getter, NULL, + "The __dict__ for this objfile.", NULL }, { "filename", objfpy_get_filename, NULL, "The objfile's filename, or None.", NULL }, { "username", objfpy_get_username, NULL, @@ -785,8 +779,8 @@ PyTypeObject objfile_object_type = 0, /*tp_hash */ 0, /*tp_call*/ 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ + gdb_py_generic_getattro, /*tp_getattro*/ + gdb_py_generic_setattro, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT, /*tp_flags*/ "GDB objfile object", /* tp_doc */ @@ -803,7 +797,7 @@ PyTypeObject objfile_object_type = 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ - offsetof (objfile_object, dict), /* tp_dictoffset */ + 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ objfpy_new, /* tp_new */ diff --git a/gdb/python/py-progspace.c b/gdb/python/py-progspace.c index ee26b761adb..a531026655f 100644 --- a/gdb/python/py-progspace.c +++ b/gdb/python/py-progspace.c @@ -29,17 +29,11 @@ #include "observable.h" #include "inferior.h" -struct pspace_object +struct pspace_object: public gdbpy__dict__wrapper { - PyObject_HEAD - /* The corresponding pspace. */ struct program_space *pspace; - /* Dictionary holding user-added attributes. - This is the __dict__ attribute of the object. */ - PyObject *dict; - /* The pretty-printer list of functions. */ PyObject *printers; @@ -758,8 +752,8 @@ GDBPY_INITIALIZE_FILE (gdbpy_initialize_pspace); static gdb_PyGetSetDef pspace_getset[] = { - { "__dict__", gdb_py_generic_dict, NULL, - "The __dict__ for this progspace.", &pspace_object_type }, + { "__dict__", gdb_py_generic_dict_getter, NULL, + "The __dict__ for this progspace.", NULL }, { "filename", pspy_get_filename, NULL, "The filename of the progspace's main symbol file, or None.", nullptr }, { "symbol_file", pspy_get_symbol_file, nullptr, @@ -821,8 +815,8 @@ PyTypeObject pspace_object_type = 0, /*tp_hash */ 0, /*tp_call*/ 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ + gdb_py_generic_getattro, /*tp_getattro*/ + gdb_py_generic_setattro, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT, /*tp_flags*/ "GDB progspace object", /* tp_doc */ @@ -839,7 +833,7 @@ PyTypeObject pspace_object_type = 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ - offsetof (pspace_object, dict), /* tp_dictoffset */ + 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ diff --git a/gdb/python/py-ref.h b/gdb/python/py-ref.h index b09d88dc30d..a8f3ff9201b 100644 --- a/gdb/python/py-ref.h +++ b/gdb/python/py-ref.h @@ -42,4 +42,44 @@ struct gdbpy_ref_policy template<typename T = PyObject> using gdbpy_ref = gdb::ref_ptr<T, gdbpy_ref_policy<T>>; +/* A wrapper class for Python extension objects that have a __dict__ attribute. + + Any Python C object extension needing __dict__ should inherit from this + class. Given that the C extension object must also be convertible to + PyObject, this wrapper class publicly inherits from PyObject as well. + + Access to the dict requires a custom getter defined via PyGetSetDef. + gdb_PyGetSetDef my_object_getset[] = + { + { "__dict__", gdb_py_generic_dict_getter, nullptr, + "The __dict__ for this object.", nullptr }, + ... + { nullptr } + }; + + It is also important to note that __dict__ is used during the attribute + look-up. Since this dictionary is not managed by Python and is not exposed + via tp_dictoffset, custom attribute getter (tp_getattro) and setter + (tp_setattro) are required to correctly redirect attribute access to the + dictionary: + - gdb_py_generic_getattro (), assigned to tp_getattro for static types, + or Py_tp_getattro for heap-allocated types. + - gdb_py_generic_setattro (), assigned to tp_setattro for static types, + or Py_tp_setattro for heap-allocated types. */ +struct gdbpy__dict__wrapper: public PyObject +{ + /* Dictionary holding user-added attributes. + This is the __dict__ attribute of the object. */ + PyObject *dict; + + /* Compute the address of the __dict__ attribute for the given PyObject. + The CLOSURE argument is unused. */ + static PyObject ** + compute_addr (PyObject *self, void *closure ATTRIBUTE_UNUSED) + { + auto *wrapper = reinterpret_cast<gdbpy__dict__wrapper *> (self); + return &wrapper->dict; + } +}; + #endif /* GDB_PYTHON_PY_REF_H */ diff --git a/gdb/python/py-type.c b/gdb/python/py-type.c index 40adbf0bb7d..1cc583b38d2 100644 --- a/gdb/python/py-type.c +++ b/gdb/python/py-type.c @@ -36,13 +36,8 @@ struct type_object: public PyObject extern PyTypeObject type_object_type; /* A Field object. */ -struct field_object -{ - PyObject_HEAD - - /* Dictionary holding our attributes. */ - PyObject *dict; -}; +struct field_object: public gdbpy__dict__wrapper +{}; extern PyTypeObject field_object_type; @@ -1707,8 +1702,8 @@ PyTypeObject type_object_type = static gdb_PyGetSetDef field_object_getset[] = { - { "__dict__", gdb_py_generic_dict, NULL, - "The __dict__ for this field.", &field_object_type }, + { "__dict__", gdb_py_generic_dict_getter, NULL, + "The __dict__ for this field.", NULL }, { NULL } }; @@ -1730,8 +1725,8 @@ PyTypeObject field_object_type = 0, /*tp_hash */ 0, /*tp_call*/ 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ + gdb_py_generic_getattro, /*tp_getattro*/ + gdb_py_generic_setattro, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT, /*tp_flags*/ "GDB field object", /* tp_doc */ @@ -1748,7 +1743,7 @@ PyTypeObject field_object_type = 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ - offsetof (field_object, dict), /* tp_dictoffset */ + 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ diff --git a/gdb/python/py-utils.c b/gdb/python/py-utils.c index 131230f80b3..81c787ac43c 100644 --- a/gdb/python/py-utils.c +++ b/gdb/python/py-utils.c @@ -309,24 +309,97 @@ gdb_py_int_as_long (PyObject *obj, long *result) \f -/* Generic implementation of the __dict__ attribute for objects that - have a dictionary. The CLOSURE argument should be the type object. - This only handles positive values for tp_dictoffset. */ +/* Generic implementation of the getter for the __dict__ attribute for objects + having a dictionary. The CLOSURE argument is unused. */ PyObject * -gdb_py_generic_dict (PyObject *self, void *closure) +gdb_py_generic_dict_getter (PyObject *self, void *closure) { - PyObject *result; - PyTypeObject *type_obj = (PyTypeObject *) closure; - char *raw_ptr; + PyObject **py_dict_ptr = gdbpy__dict__wrapper::compute_addr (self, closure); + PyObject *py_dict = *py_dict_ptr; + if (py_dict == nullptr) + { + PyErr_SetString (PyExc_AttributeError, + "This object has no __dict__"); + return nullptr; + } + return Py_NewRef (py_dict); +} - raw_ptr = (char *) self + type_obj->tp_dictoffset; - result = * (PyObject **) raw_ptr; +/* Generic implementation of the setter for the __dict__ attribute for objects + having a dictionary. The CLOSURE argument is unused. */ - Py_INCREF (result); - return result; +int +gdb_py_generic_dict_setter (PyObject *self, PyObject *value, void *closure) +{ + if (value == nullptr) + { + PyErr_SetString (PyExc_TypeError, "cannot delete __dict__"); + return -1; + } + + PyObject **py_dict_ptr = gdbpy__dict__wrapper::compute_addr (self, closure); + + /* Delete the old value (if there is one). */ + if (*py_dict_ptr != nullptr) + Py_DECREF (*py_dict_ptr); + + *py_dict_ptr = Py_NewRef (value); + return 0; +} + +/* Generic attribute getter function similar to PyObject_GenericGetAttr () but + that should be used when the object has a dictionary __dict__. */ +PyObject * +gdb_py_generic_getattro (PyObject *self, PyObject *attr) +{ + PyObject *value = PyObject_GenericGetAttr (self, attr); + if (value != nullptr) + return value; + + if (! PyErr_ExceptionMatches (PyExc_AttributeError)) + return nullptr; + + /* Clear previous AttributeError set by PyObject_GenericGetAttr. */ + PyErr_Clear(); + + gdbpy_ref<> dict (gdb_py_generic_dict_getter (self, nullptr)); + value = PyDict_GetItemWithError (dict.get (), attr); + if (value != nullptr) + return Py_NewRef (value); + + PyErr_Format (PyExc_AttributeError, + "'%s' object has no attribute '%s'", + Py_TYPE (self)->tp_name, + PyUnicode_AsUTF8AndSize (attr, nullptr)); + return nullptr; } +/* Generic attribute setter function similar to PyObject_GenericSetAttr () but + that should be used when the object has a dictionary __dict__. */ +int +gdb_py_generic_setattro (PyObject *self, PyObject *attr, PyObject *value) +{ + if (PyObject_GenericSetAttr (self, attr, value) == 0) + return 0; + + if (! PyErr_ExceptionMatches (PyExc_AttributeError)) + return -1; + + /* Clear previous AttributeError set by PyObject_GenericGetAttr. */ + PyErr_Clear(); + + gdbpy_ref<> dict (gdb_py_generic_dict_getter (self, nullptr)); + /* Delete the old value (if there is one). */ + PyObject *old_value = PyDict_GetItem (dict.get (), attr); + if (old_value != nullptr) + Py_DECREF (old_value); + /* Set the new value. */ + return PyDict_SetItem (dict.get (), attr, value); +} + +\f + /* Like PyModule_AddObject, but does not steal a reference to OBJECT. */ diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h index 95619bf775e..cde2dfa2d52 100644 --- a/gdb/python/python-internal.h +++ b/gdb/python/python-internal.h @@ -107,6 +107,15 @@ typedef unsigned long gdb_py_ulongest; #endif /* HAVE_LONG_LONG */ +#if PY_VERSION_HEX < 0x030a0000 +static inline PyObject* +Py_NewRef (PyObject *obj) +{ + Py_INCREF (obj); + return obj; +} +#endif + /* A template variable holding the format character (as for Py_BuildValue) for a given type. */ template<typename T> @@ -384,22 +393,27 @@ struct gdbpy_breakpoint_object extern gdbpy_breakpoint_object *bppy_pending_object; -struct thread_object +struct thread_object: public gdbpy__dict__wrapper { - PyObject_HEAD - /* The thread we represent. */ struct thread_info *thread; /* The Inferior object to which this thread belongs. */ PyObject *inf_obj; - - /* Dictionary holding user-added attributes. This is the __dict__ - attribute of the object. */ - PyObject *dict; }; -struct inferior_object; +using thread_map_t + = gdb::unordered_map<thread_info *, gdbpy_ref<thread_object>>; + +struct inferior_object: public gdbpy__dict__wrapper +{ + /* The inferior we represent. */ + struct inferior *inferior; + + /* thread_object instances under this inferior. This owns a + reference to each object it contains. */ + thread_map_t *threads; +}; extern struct cmd_list_element *set_python_list; extern struct cmd_list_element *show_python_list; @@ -989,7 +1003,10 @@ gdbpy_ref<> gdb_py_object_from_longest (LONGEST l); gdbpy_ref<> gdb_py_object_from_ulongest (ULONGEST l); int gdb_py_int_as_long (PyObject *, long *); -PyObject *gdb_py_generic_dict (PyObject *self, void *closure); +PyObject *gdb_py_generic_dict_getter (PyObject *self, void *closure); +int gdb_py_generic_dict_setter (PyObject *self, PyObject *value, void *closure); +PyObject *gdb_py_generic_getattro (PyObject *self, PyObject *attr); +int gdb_py_generic_setattro (PyObject *self, PyObject *attr, PyObject *value); int gdb_pymodule_addobject (PyObject *mod, const char *name, PyObject *object); -- 2.52.0 ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2 4/6] gdb: new setters and getters for __dict__, and attributes 2026-01-27 17:02 ` [PATCH v2 4/6] gdb: new setters and getters for __dict__, and attributes Matthieu Longo @ 2026-01-27 19:06 ` Tom Tromey 2026-01-28 11:57 ` Matthieu Longo 2026-01-28 17:43 ` Matthieu Longo 0 siblings, 2 replies; 20+ messages in thread From: Tom Tromey @ 2026-01-27 19:06 UTC (permalink / raw) To: Matthieu Longo; +Cc: gdb-patches, Tom Tromey >>>>> "Matthieu" == Matthieu Longo <matthieu.longo@arm.com> writes: Matthieu> GDB is currently using the Python unlimited API. Migrating the codebase Matthieu> to the Python limited API would have for benefit to make a GDB build Matthieu> artifacts compatible with older and newer versions of Python that they Matthieu> were built with. Matthieu> This patch prepares the ground for migrating the existing C extension Matthieu> types from static types to heap-allocated ones, by removing the Matthieu> dependency on tp_dictoffset, which is unavailable when using the limited Matthieu> API. Thanks. Matthieu> - gdbpy__dict__wrapper: a base class for C extension objects that own a Matthieu> __dict__. I think a double underscore in a name is reserved by the C++ implementation, so "gdbpy__dict__wrapper" can't be used. I think the same name with single underscores would be fine. Matthieu> - gdb_py_generic_dict_setter: a __dict__ setter provided for completeness. Matthieu> It should not be used, as __dict__ should be read-only after the object Matthieu> initialization. If it shouldn't be used, it should just not exist, I think. Matthieu> -using thread_map_t Matthieu> - = gdb::unordered_map<thread_info *, gdbpy_ref<thread_object>>; Matthieu> - Matthieu> -struct inferior_object Matthieu> -{ Matthieu> - PyObject_HEAD This moved to python-internal.h, but I don't understand why that move is needed. Matthieu> + /* Compute the address of the __dict__ attribute for the given PyObject. Matthieu> + The CLOSURE argument is unused. */ Matthieu> + static PyObject ** Matthieu> + compute_addr (PyObject *self, void *closure ATTRIBUTE_UNUSED) In classes there doesn't have to be a newline before the method name. Also since the closure isn't used here it should probably just be dropped. If it's needed in the future we can add it then, but I'm going to guess it won't be. Matthieu> +/* Generic attribute getter function similar to PyObject_GenericGetAttr () but Matthieu> + that should be used when the object has a dictionary __dict__. */ Matthieu> +PyObject * Matthieu> +gdb_py_generic_getattro (PyObject *self, PyObject *attr) Matthieu> +{ Matthieu> + PyObject *value = PyObject_GenericGetAttr (self, attr); Matthieu> + if (value != nullptr) Matthieu> + return value; Matthieu> + Matthieu> + if (! PyErr_ExceptionMatches (PyExc_AttributeError)) Matthieu> + return nullptr; Matthieu> + Matthieu> + /* Clear previous AttributeError set by PyObject_GenericGetAttr. */ Matthieu> + PyErr_Clear(); Matthieu> + Matthieu> + gdbpy_ref<> dict (gdb_py_generic_dict_getter (self, nullptr)); Matthieu> + value = PyDict_GetItemWithError (dict.get (), attr); I think this has to do check 'dict == nullptr' before this call. Matthieu> + if (value != nullptr) Matthieu> + return Py_NewRef (value); Matthieu> + Matthieu> + PyErr_Format (PyExc_AttributeError, Matthieu> + "'%s' object has no attribute '%s'", Matthieu> + Py_TYPE (self)->tp_name, Matthieu> + PyUnicode_AsUTF8AndSize (attr, nullptr)); Matthieu> + return nullptr; PyDict_GetItemWithError sets the error, so is this just here to make a nicer message? That's fine if so, though I am not sure if you have to clear the existing error here first? Matthieu> + gdbpy_ref<> dict (gdb_py_generic_dict_getter (self, nullptr)); Matthieu> + /* Delete the old value (if there is one). */ Matthieu> + PyObject *old_value = PyDict_GetItem (dict.get (), attr); dict==nullptr check Matthieu> + if (old_value != nullptr) Matthieu> + Py_DECREF (old_value); Matthieu> + /* Set the new value. */ Matthieu> + return PyDict_SetItem (dict.get (), attr, value); I think this ordering means self-assignment can break. IMO it's better to use gdbpy_ref to manage the lifetime of the old value. Matthieu> +#if PY_VERSION_HEX < 0x030a0000 Matthieu> +static inline PyObject* Matthieu> +Py_NewRef (PyObject *obj) The return type is missing a space before the "*" Tom ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2 4/6] gdb: new setters and getters for __dict__, and attributes 2026-01-27 19:06 ` Tom Tromey @ 2026-01-28 11:57 ` Matthieu Longo 2026-01-28 17:43 ` Matthieu Longo 1 sibling, 0 replies; 20+ messages in thread From: Matthieu Longo @ 2026-01-28 11:57 UTC (permalink / raw) To: Tom Tromey; +Cc: gdb-patches On 27/01/2026 19:06, Tom Tromey wrote: >>>>>> "Matthieu" == Matthieu Longo <matthieu.longo@arm.com> writes: > > Matthieu> GDB is currently using the Python unlimited API. Migrating the codebase > Matthieu> to the Python limited API would have for benefit to make a GDB build > Matthieu> artifacts compatible with older and newer versions of Python that they > Matthieu> were built with. > > Matthieu> This patch prepares the ground for migrating the existing C extension > Matthieu> types from static types to heap-allocated ones, by removing the > Matthieu> dependency on tp_dictoffset, which is unavailable when using the limited > Matthieu> API. > > Thanks. > > Matthieu> - gdbpy__dict__wrapper: a base class for C extension objects that own a > Matthieu> __dict__. > > I think a double underscore in a name is reserved by the C++ > implementation, so "gdbpy__dict__wrapper" can't be used. I think the > same name with single underscores would be fine. > Renamed. > Matthieu> - gdb_py_generic_dict_setter: a __dict__ setter provided for completeness. > Matthieu> It should not be used, as __dict__ should be read-only after the object > Matthieu> initialization. > > If it shouldn't be used, it should just not exist, I think. > Removed. > Matthieu> -using thread_map_t > Matthieu> - = gdb::unordered_map<thread_info *, gdbpy_ref<thread_object>>; > Matthieu> - > Matthieu> -struct inferior_object > Matthieu> -{ > Matthieu> - PyObject_HEAD > > This moved to python-internal.h, but I don't understand why that move > is needed. > > Matthieu> + /* Compute the address of the __dict__ attribute for the given PyObject. > Matthieu> + The CLOSURE argument is unused. */ > Matthieu> + static PyObject ** > Matthieu> + compute_addr (PyObject *self, void *closure ATTRIBUTE_UNUSED) > > In classes there doesn't have to be a newline before the method name. > Also since the closure isn't used here it should probably just be > dropped. If it's needed in the future we can add it then, but I'm going > to guess it won't be. > Fixed. > Matthieu> +/* Generic attribute getter function similar to PyObject_GenericGetAttr () but > Matthieu> + that should be used when the object has a dictionary __dict__. */ > Matthieu> +PyObject * > Matthieu> +gdb_py_generic_getattro (PyObject *self, PyObject *attr) > Matthieu> +{ > Matthieu> + PyObject *value = PyObject_GenericGetAttr (self, attr); > Matthieu> + if (value != nullptr) > Matthieu> + return value; > Matthieu> + > Matthieu> + if (! PyErr_ExceptionMatches (PyExc_AttributeError)) > Matthieu> + return nullptr; > Matthieu> + > Matthieu> + /* Clear previous AttributeError set by PyObject_GenericGetAttr. */ > Matthieu> + PyErr_Clear(); > Matthieu> + Also, in both gdb_py_generic_getattro() and gdb_py_generic_setattro(), I moved the look-up of __dict__ before PyErr_Clear(). If the look-up of __dict__ were to fail for some reason, we still want the exception raised by PyObject_GenericGetAttr / PyObject_GenericSetAttr to be there. > Matthieu> + gdbpy_ref<> dict (gdb_py_generic_dict_getter (self, nullptr)); > Matthieu> + value = PyDict_GetItemWithError (dict.get (), attr); > > I think this has to do check 'dict == nullptr' before this call. > Indeed, I forgot that. Fixed. > Matthieu> + if (value != nullptr) > Matthieu> + return Py_NewRef (value); > Matthieu> + > Matthieu> + PyErr_Format (PyExc_AttributeError, > Matthieu> + "'%s' object has no attribute '%s'", > Matthieu> + Py_TYPE (self)->tp_name, > Matthieu> + PyUnicode_AsUTF8AndSize (attr, nullptr)); > Matthieu> + return nullptr; > > PyDict_GetItemWithError sets the error, so is this just here to make a > nicer message? That's fine if so, though I am not sure if you have to > clear the existing error here first? > https://github.com/python/cpython/blob/08d7bd28fecca524c648dda240022add704b8f8a/Objects/dictobject.c#L2505 /* Variant of PyDict_GetItem() that doesn't suppress exceptions. This returns NULL *with* an exception set if an exception occurred. It returns NULL *without* an exception set if the key wasn't present. */ PyDict_GetItemWithError has a misleading name, because if the key is not found, no PyExc_AttributeError is set. This situation results in a new exception being raised: Python Exception <class 'SystemError'>: error return without exception set Hence this surprising manual setting of an exception to makes everything fit into place. I am going to add a comment to clarify the intent, because I had even forgotten why I did this in the first place. > Matthieu> + gdbpy_ref<> dict (gdb_py_generic_dict_getter (self, nullptr)); > Matthieu> + /* Delete the old value (if there is one). */ > Matthieu> + PyObject *old_value = PyDict_GetItem (dict.get (), attr); > > dict==nullptr check > Fixed. > Matthieu> + if (old_value != nullptr) > Matthieu> + Py_DECREF (old_value); > Matthieu> + /* Set the new value. */ > Matthieu> + return PyDict_SetItem (dict.get (), attr, value); > > I think this ordering means self-assignment can break. IMO it's better > to use gdbpy_ref to manage the lifetime of the old value. > It seems that I got the whole thing wrong with my assumption that I needed to decref the old value. This is already managed in insertdict() called from PyDict_SetItem() https://github.com/python/cpython/blob/08d7bd28fecca524c648dda240022add704b8f8a/Objects/dictobject.c#L1934 This PyDict_GetItem () is useless, I will remove it. > Matthieu> +#if PY_VERSION_HEX < 0x030a0000 > Matthieu> +static inline PyObject* > Matthieu> +Py_NewRef (PyObject *obj) > > The return type is missing a space before the "*" > Fixed. > Tom Since all others patches are approved with the small modifications you suggested, I am going to only post a new revision of this patch in this same thread to avoid a new round. Matthieu ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2 4/6] gdb: new setters and getters for __dict__, and attributes 2026-01-27 19:06 ` Tom Tromey 2026-01-28 11:57 ` Matthieu Longo @ 2026-01-28 17:43 ` Matthieu Longo 2026-01-28 17:51 ` Tom Tromey 1 sibling, 1 reply; 20+ messages in thread From: Matthieu Longo @ 2026-01-28 17:43 UTC (permalink / raw) To: Tom Tromey; +Cc: gdb-patches On 27/01/2026 19:06, Tom Tromey wrote: > Matthieu> -using thread_map_t > Matthieu> - = gdb::unordered_map<thread_info *, gdbpy_ref<thread_object>>; > Matthieu> - > Matthieu> -struct inferior_object > Matthieu> -{ > Matthieu> - PyObject_HEAD > > This moved to python-internal.h, but I don't understand why that move > is needed. I moved the declaration to python-internal.h because of the following build issue after adding the static_assert in gdbpy_ref_policy. CXX python/py-exitedevent.o In file included from /usr/include/c++/14/bits/move.h:37, from /usr/include/c++/14/bits/stl_function.h:60, from /usr/include/c++/14/functional:49, from ../../gdb/../gdbsupport/ptid.h:35, from ../../gdb/../gdbsupport/common-defs.h:212, from ./../../gdb/defs.h:26, from <command-line>: /usr/include/c++/14/type_traits: In instantiation of ‘struct std::is_base_of<_object, inferior_object>’: ../../gdb/python/py-ref.h:29:47: required from ‘struct gdbpy_ref_policy<inferior_object>’ 29 | static_assert(std::is_base_of<PyObject, T>::value, | ^~~~~ ../../gdb/python/py-exitedevent.c:42:18: required from here 42 | if (inf_obj == NULL || evpy_add_attribute (exited_event.get (), | ^~~~ /usr/include/c++/14/type_traits:1493:30: error: invalid use of incomplete type ‘struct inferior_object’ 1493 | : public __bool_constant<__is_base_of(_Base, _Derived)> | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In file included from ../../gdb/python/py-events.h:24, from ../../gdb/python/py-event.h:23, from ../../gdb/python/py-exitedevent.c:20: ../../gdb/python/python-internal.h:405:8: note: forward declaration of ‘struct inferior_object’ 405 | struct inferior_object; | ^~~~~~~~~~~~~~~ In file included from ../../gdb/python/python-internal.h:61: ../../gdb/python/py-ref.h: In instantiation of ‘struct gdbpy_ref_policy<inferior_object>’: ../../gdb/python/py-exitedevent.c:42:18: required from here 42 | if (inf_obj == NULL || evpy_add_attribute (exited_event.get (), | ^~~~ ../../gdb/python/py-ref.h:29:47: error: ‘value’ is not a member of ‘std::is_base_of<_object, inferior_object>’ 29 | static_assert(std::is_base_of<PyObject, T>::value, | ^~~~~ ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2 4/6] gdb: new setters and getters for __dict__, and attributes 2026-01-28 17:43 ` Matthieu Longo @ 2026-01-28 17:51 ` Tom Tromey 0 siblings, 0 replies; 20+ messages in thread From: Tom Tromey @ 2026-01-28 17:51 UTC (permalink / raw) To: Matthieu Longo; +Cc: Tom Tromey, gdb-patches >>>>> "Matthieu" == Matthieu Longo <matthieu.longo@arm.com> writes: Matthieu> I moved the declaration to python-internal.h because of the Matthieu> following build issue after adding the static_assert in Matthieu> gdbpy_ref_policy. Ok, I see, thank you. Tom ^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH v2 5/6] gdb: cast all Python extension objects passed to gdbpy_ref_policy to PyObject* 2026-01-27 17:02 [PATCH v2 0/6] gdb: minor fixes for Python limited C API support Matthieu Longo ` (3 preceding siblings ...) 2026-01-27 17:02 ` [PATCH v2 4/6] gdb: new setters and getters for __dict__, and attributes Matthieu Longo @ 2026-01-27 17:02 ` Matthieu Longo 2026-01-27 18:28 ` Tom Tromey 2026-01-27 17:02 ` [PATCH v2 6/6] gdb: make remaining Python extension objects inherit from PyObject Matthieu Longo 5 siblings, 1 reply; 20+ messages in thread From: Matthieu Longo @ 2026-01-27 17:02 UTC (permalink / raw) To: gdb-patches; +Cc: Tom Tromey, Matthieu Longo When enabling the Python limited API, pointers to Python C extension objects can no longer be implicitly converted to 'PyObject *' by the compiler. gdbpy_ref_policy is a templated class that provides a generic interface for incrementing and decrementing the reference counter on the given object. It is used as a specialisation of the policy parameter in gdb::ref_ptr, together with PyObject as the parameter type. As a result, gdbpy_ref_policy always expects an argument derived from PyObject. This patch fixes the resulting compilation issue by adding an explicit static_cast to 'PyObject *' before passing the value to Py_INCREF and Py_DECREF. As a side effect, these casts enforce, at compile time, that the template type passed to gdbpy_ref_policy is a subclass of PyObject. To provide a clearer diagnostic when an incorrect type is used, a static_assert is added to gdbpy_ref_policy, avoiding obscure errors originating from the static_cast. Finally, all C Python extension types passed to gdbpy_ref_policy are updated to inherit from PyObject. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=23830 --- gdb/python/py-breakpoint.c | 4 +--- gdb/python/py-cmd.c | 4 +--- gdb/python/py-color.c | 4 +--- gdb/python/py-connection.c | 4 +--- gdb/python/py-corefile.c | 8 ++------ gdb/python/py-disasm.c | 8 ++------ gdb/python/py-events.h | 4 +--- gdb/python/py-frame.c | 4 ++-- gdb/python/py-membuf.c | 5 ++--- gdb/python/py-micmd.c | 4 +--- gdb/python/py-ref.h | 7 +++++-- gdb/python/py-registers.c | 10 ++++------ gdb/python/py-tui.c | 4 +--- gdb/python/python-internal.h | 4 +--- 14 files changed, 25 insertions(+), 49 deletions(-) diff --git a/gdb/python/py-breakpoint.c b/gdb/python/py-breakpoint.c index fc53213966a..3953c441c5c 100644 --- a/gdb/python/py-breakpoint.c +++ b/gdb/python/py-breakpoint.c @@ -37,10 +37,8 @@ extern PyTypeObject breakpoint_location_object_type; -struct gdbpy_breakpoint_location_object +struct gdbpy_breakpoint_location_object: public PyObject { - PyObject_HEAD - /* An owning reference to the gdb breakpoint location object. */ bp_location *bp_loc; diff --git a/gdb/python/py-cmd.c b/gdb/python/py-cmd.c index 6b40b1796c6..88bc150b765 100644 --- a/gdb/python/py-cmd.c +++ b/gdb/python/py-cmd.c @@ -50,10 +50,8 @@ static const struct cmdpy_completer completers[] = /* A gdb command. For the time being only ordinary commands (not set/show commands) are allowed. */ -struct cmdpy_object +struct cmdpy_object: public PyObject { - PyObject_HEAD - /* The corresponding gdb command object, or NULL if the command is no longer installed. */ struct cmd_list_element *command; diff --git a/gdb/python/py-color.c b/gdb/python/py-color.c index b41dff2edd7..e174a7b6858 100644 --- a/gdb/python/py-color.c +++ b/gdb/python/py-color.c @@ -37,10 +37,8 @@ static struct { }; /* A color. */ -struct colorpy_object +struct colorpy_object: public PyObject { - PyObject_HEAD - /* Underlying value. */ ui_file_style::color color; }; diff --git a/gdb/python/py-connection.c b/gdb/python/py-connection.c index bef50b07f18..e42939a995d 100644 --- a/gdb/python/py-connection.c +++ b/gdb/python/py-connection.c @@ -31,10 +31,8 @@ /* The Python object that represents a connection. */ -struct connection_object +struct connection_object: public PyObject { - PyObject_HEAD - /* The process target that represents this connection. When a connection_object is created this field will always point at a valid target. Later, if GDB stops using this target (the target is popped diff --git a/gdb/python/py-corefile.c b/gdb/python/py-corefile.c index 4a6982231c8..622456aaae3 100644 --- a/gdb/python/py-corefile.c +++ b/gdb/python/py-corefile.c @@ -45,10 +45,8 @@ extern PyTypeObject corefile_object_type; /* A gdb.CorefileMapped object. */ -struct corefile_mapped_file_object +struct corefile_mapped_file_object: public PyObject { - PyObject_HEAD - /* The name of a file that was mapped when the core file was created. This is a 'str' object. */ PyObject *filename; @@ -70,10 +68,8 @@ extern PyTypeObject corefile_mapped_file_object_type; /* A gdb.CorefileMappedFileRegion object. */ -struct corefile_mapped_file_region_object +struct corefile_mapped_file_region_object: public PyObject { - PyObject_HEAD - /* The start and end addresses for this mapping, these are addresses within the inferior's address space. */ CORE_ADDR start; diff --git a/gdb/python/py-disasm.c b/gdb/python/py-disasm.c index c1aa4d0586a..037223f05e3 100644 --- a/gdb/python/py-disasm.c +++ b/gdb/python/py-disasm.c @@ -28,10 +28,8 @@ /* Implement gdb.disassembler.DisassembleInfo type. An object of this type represents a single disassembler request from GDB. */ -struct disasm_info_object +struct disasm_info_object: public PyObject { - PyObject_HEAD - /* The architecture in which we are disassembling. */ struct gdbarch *gdbarch; @@ -99,10 +97,8 @@ extern PyTypeObject disasm_part_object_type; the disassembled instruction (in bytes), and the string representing the disassembled instruction. */ -struct disasm_result_object +struct disasm_result_object: public PyObject { - PyObject_HEAD - /* The length of the disassembled instruction in bytes. */ int length; diff --git a/gdb/python/py-events.h b/gdb/python/py-events.h index 9fc7a86920e..a923a923e56 100644 --- a/gdb/python/py-events.h +++ b/gdb/python/py-events.h @@ -27,10 +27,8 @@ /* Stores a list of objects to be notified when the event for which this registry tracks occurs. */ -struct eventregistry_object +struct eventregistry_object: public PyObject { - PyObject_HEAD - PyObject *callbacks; }; diff --git a/gdb/python/py-frame.c b/gdb/python/py-frame.c index 027ccb4112d..bb61b058671 100644 --- a/gdb/python/py-frame.c +++ b/gdb/python/py-frame.c @@ -28,8 +28,8 @@ #include "symfile.h" #include "objfiles.h" -struct frame_object { - PyObject_HEAD +struct frame_object: public PyObject +{ struct frame_id frame_id; struct gdbarch *gdbarch; diff --git a/gdb/python/py-membuf.c b/gdb/python/py-membuf.c index 832ab62cf52..46033a0dda3 100644 --- a/gdb/python/py-membuf.c +++ b/gdb/python/py-membuf.c @@ -19,9 +19,8 @@ #include "python-internal.h" -struct membuf_object { - PyObject_HEAD - +struct membuf_object: public PyObject +{ /* Pointer to the raw data, and array of gdb_bytes. */ void *buffer; diff --git a/gdb/python/py-micmd.c b/gdb/python/py-micmd.c index c6a96dc3ad5..2b4b083d0e3 100644 --- a/gdb/python/py-micmd.c +++ b/gdb/python/py-micmd.c @@ -55,10 +55,8 @@ struct mi_command_py; /* Representation of a Python gdb.MICommand object. */ -struct micmdpy_object +struct micmdpy_object: public PyObject { - PyObject_HEAD - /* The object representing this command in the MI command table. This pointer can be nullptr if the command is not currently installed into the MI command table (see gdb.MICommand.installed property). */ diff --git a/gdb/python/py-ref.h b/gdb/python/py-ref.h index a8f3ff9201b..7594d4a9fc3 100644 --- a/gdb/python/py-ref.h +++ b/gdb/python/py-ref.h @@ -26,14 +26,17 @@ template<typename T> struct gdbpy_ref_policy { + static_assert(std::is_base_of<PyObject, T>::value, + "T must be a subclass of PyObject"); + static void incref (T *ptr) { - Py_INCREF (ptr); + Py_INCREF (static_cast<PyObject *> (ptr)); } static void decref (T *ptr) { - Py_DECREF (ptr); + Py_DECREF (static_cast<PyObject *> (ptr)); } }; diff --git a/gdb/python/py-registers.c b/gdb/python/py-registers.c index ac7480caea9..30b333c444c 100644 --- a/gdb/python/py-registers.c +++ b/gdb/python/py-registers.c @@ -49,9 +49,8 @@ struct register_descriptor_iterator_object { extern PyTypeObject register_descriptor_iterator_object_type; /* A register descriptor. */ -struct register_descriptor_object { - PyObject_HEAD - +struct register_descriptor_object: public PyObject +{ /* The register this is a descriptor for. */ int regnum; @@ -75,9 +74,8 @@ struct reggroup_iterator_object { extern PyTypeObject reggroup_iterator_object_type; /* A register group object. */ -struct reggroup_object { - PyObject_HEAD - +struct reggroup_object: public PyObject +{ /* The register group being described. */ const struct reggroup *reggroup; }; diff --git a/gdb/python/py-tui.c b/gdb/python/py-tui.c index 578ddfbcc67..be19193770f 100644 --- a/gdb/python/py-tui.c +++ b/gdb/python/py-tui.c @@ -44,10 +44,8 @@ class tui_py_window; /* A PyObject representing a TUI window. */ -struct gdbpy_tui_window +struct gdbpy_tui_window: public PyObject { - PyObject_HEAD - /* The TUI window, or nullptr if the window has been deleted. */ tui_py_window *window; diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h index cde2dfa2d52..1b4dcd4c5fe 100644 --- a/gdb/python/python-internal.h +++ b/gdb/python/python-internal.h @@ -350,10 +350,8 @@ extern PyTypeObject thread_object_type; extern bool gdbpy_breakpoint_init_breakpoint_type (); -struct gdbpy_breakpoint_object +struct gdbpy_breakpoint_object: public PyObject { - PyObject_HEAD - /* The breakpoint number according to gdb. */ int number; -- 2.52.0 ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2 5/6] gdb: cast all Python extension objects passed to gdbpy_ref_policy to PyObject* 2026-01-27 17:02 ` [PATCH v2 5/6] gdb: cast all Python extension objects passed to gdbpy_ref_policy to PyObject* Matthieu Longo @ 2026-01-27 18:28 ` Tom Tromey 2026-01-28 11:58 ` Matthieu Longo 0 siblings, 1 reply; 20+ messages in thread From: Tom Tromey @ 2026-01-27 18:28 UTC (permalink / raw) To: Matthieu Longo; +Cc: gdb-patches, Tom Tromey >>>>> "Matthieu" == Matthieu Longo <matthieu.longo@arm.com> writes: Matthieu> +struct gdbpy_breakpoint_location_object: public PyObject gdb style is spaces before and after the ":". This is ok with this fixed, it occurs throughout the patch. Approved-By: Tom Tromey <tom@tromey.com> Tom ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2 5/6] gdb: cast all Python extension objects passed to gdbpy_ref_policy to PyObject* 2026-01-27 18:28 ` Tom Tromey @ 2026-01-28 11:58 ` Matthieu Longo 0 siblings, 0 replies; 20+ messages in thread From: Matthieu Longo @ 2026-01-28 11:58 UTC (permalink / raw) To: Tom Tromey; +Cc: gdb-patches On 27/01/2026 18:28, Tom Tromey wrote: >>>>>> "Matthieu" == Matthieu Longo <matthieu.longo@arm.com> writes: > > Matthieu> +struct gdbpy_breakpoint_location_object: public PyObject > > gdb style is spaces before and after the ":". > > This is ok with this fixed, it occurs throughout the patch. > Approved-By: Tom Tromey <tom@tromey.com> > > Tom Fixed. Matthieu ^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH v2 6/6] gdb: make remaining Python extension objects inherit from PyObject 2026-01-27 17:02 [PATCH v2 0/6] gdb: minor fixes for Python limited C API support Matthieu Longo ` (4 preceding siblings ...) 2026-01-27 17:02 ` [PATCH v2 5/6] gdb: cast all Python extension objects passed to gdbpy_ref_policy to PyObject* Matthieu Longo @ 2026-01-27 17:02 ` Matthieu Longo 2026-01-27 18:29 ` Tom Tromey 5 siblings, 1 reply; 20+ messages in thread From: Matthieu Longo @ 2026-01-27 17:02 UTC (permalink / raw) To: gdb-patches; +Cc: Tom Tromey, Matthieu Longo Previous patches made some Python extension objects ipublicly inherit directly or indirectly from PyObject. In the interest of consistency, this patch makes all remaining Python extension objects still not inheriting from PyObject do so. --- gdb/python/py-arch.c | 4 ++-- gdb/python/py-block.c | 8 ++++---- gdb/python/py-disasm.c | 8 ++------ gdb/python/py-instruction.c | 5 ++--- gdb/python/py-lazy-string.c | 5 ++--- gdb/python/py-linetable.c | 12 ++++++------ gdb/python/py-param.c | 4 +--- gdb/python/py-prettyprint.c | 6 ++---- gdb/python/py-record-btrace.c | 5 ++--- gdb/python/py-record.c | 4 +--- gdb/python/py-record.h | 8 ++------ gdb/python/py-registers.c | 10 ++++------ gdb/python/py-style.c | 4 +--- gdb/python/py-type.c | 4 ++-- gdb/python/py-unwind.c | 8 ++------ gdb/python/py-value.c | 4 ++-- 16 files changed, 37 insertions(+), 62 deletions(-) diff --git a/gdb/python/py-arch.c b/gdb/python/py-arch.c index 765a21ae0b7..f761c7ba30b 100644 --- a/gdb/python/py-arch.c +++ b/gdb/python/py-arch.c @@ -22,8 +22,8 @@ #include "disasm.h" #include "python-internal.h" -struct arch_object { - PyObject_HEAD +struct arch_object: public PyObject +{ struct gdbarch *gdbarch; }; diff --git a/gdb/python/py-block.c b/gdb/python/py-block.c index 3a59d767b5c..4b02115a5f9 100644 --- a/gdb/python/py-block.c +++ b/gdb/python/py-block.c @@ -23,8 +23,8 @@ #include "python-internal.h" #include "objfiles.h" -struct block_object { - PyObject_HEAD +struct block_object: public PyObject +{ /* The GDB block structure that represents a frame's code block. */ const struct block *block; /* The backing object file. There is no direct relationship in GDB @@ -33,8 +33,8 @@ struct block_object { struct objfile *objfile; }; -struct block_syms_iterator_object { - PyObject_HEAD +struct block_syms_iterator_object: public PyObject +{ /* The block. */ const struct block *block; /* The iterator for that block. */ diff --git a/gdb/python/py-disasm.c b/gdb/python/py-disasm.c index 037223f05e3..8b796628d72 100644 --- a/gdb/python/py-disasm.c +++ b/gdb/python/py-disasm.c @@ -57,10 +57,8 @@ extern PyTypeObject disasm_info_object_type; that is an address that should be printed using a call to GDB's internal print_address function. */ -struct disasm_addr_part_object +struct disasm_addr_part_object: public PyObject { - PyObject_HEAD - /* The address to be formatted. */ bfd_vma address; @@ -77,10 +75,8 @@ extern PyTypeObject disasm_addr_part_object_type; this type represents a small part of a disassembled instruction; a part that is a piece of test along with an associated style. */ -struct disasm_text_part_object +struct disasm_text_part_object: public PyObject { - PyObject_HEAD - /* The string that is this part. */ std::string *string; diff --git a/gdb/python/py-instruction.c b/gdb/python/py-instruction.c index b7ab6e4668f..df64d85988f 100644 --- a/gdb/python/py-instruction.c +++ b/gdb/python/py-instruction.c @@ -29,9 +29,8 @@ PyTypeObject py_insn_type = { /* Python instruction object. */ -struct py_insn_obj { - PyObject_HEAD -}; +struct py_insn_obj: public PyObject +{}; /* Getter function for gdb.Instruction attributes. */ diff --git a/gdb/python/py-lazy-string.c b/gdb/python/py-lazy-string.c index 41f958744c1..3e95a041dcc 100644 --- a/gdb/python/py-lazy-string.c +++ b/gdb/python/py-lazy-string.c @@ -23,9 +23,8 @@ #include "valprint.h" #include "language.h" -struct lazy_string_object { - PyObject_HEAD - +struct lazy_string_object: public PyObject +{ /* Holds the address of the lazy string. */ CORE_ADDR address; diff --git a/gdb/python/py-linetable.c b/gdb/python/py-linetable.c index 0ea2d843dd3..2db3488b1d3 100644 --- a/gdb/python/py-linetable.c +++ b/gdb/python/py-linetable.c @@ -19,8 +19,8 @@ #include "python-internal.h" -struct linetable_entry_object { - PyObject_HEAD +struct linetable_entry_object: public PyObject +{ /* The line table source line. */ int line; /* The pc associated with the source line. */ @@ -29,8 +29,8 @@ struct linetable_entry_object { extern PyTypeObject linetable_entry_object_type; -struct linetable_object { - PyObject_HEAD +struct linetable_object: public PyObject +{ /* The symtab python object. We store the Python object here as the underlying symtab can become invalid, and we have to run validity checks on it. */ @@ -39,8 +39,8 @@ struct linetable_object { extern PyTypeObject linetable_object_type; -struct ltpy_iterator_object { - PyObject_HEAD +struct ltpy_iterator_object: public PyObject +{ /* The current entry in the line table for the iterator */ int current_index; /* Pointer back to the original source line table object. Needed to diff --git a/gdb/python/py-param.c b/gdb/python/py-param.c index f305661fa43..4f61b8efd3c 100644 --- a/gdb/python/py-param.c +++ b/gdb/python/py-param.c @@ -124,10 +124,8 @@ union parmpy_variable }; /* A GDB parameter. */ -struct parmpy_object +struct parmpy_object: public PyObject { - PyObject_HEAD - /* The type of the parameter. */ enum var_types type; diff --git a/gdb/python/py-prettyprint.c b/gdb/python/py-prettyprint.c index a920e6f3cf7..8895f1c8771 100644 --- a/gdb/python/py-prettyprint.c +++ b/gdb/python/py-prettyprint.c @@ -783,10 +783,8 @@ gdbpy_get_print_options (value_print_options *opts) /* A ValuePrinter is just a "tag", so it has no state other than that required by Python. */ -struct printer_object -{ - PyObject_HEAD -}; +struct printer_object: public PyObject +{}; /* The ValuePrinter type object. */ PyTypeObject printer_object_type = diff --git a/gdb/python/py-record-btrace.c b/gdb/python/py-record-btrace.c index d9534c02331..637488d797c 100644 --- a/gdb/python/py-record-btrace.c +++ b/gdb/python/py-record-btrace.c @@ -29,9 +29,8 @@ /* Python object for btrace record lists. */ -struct btpy_list_object { - PyObject_HEAD - +struct btpy_list_object: public PyObject +{ /* The thread this list belongs to. */ thread_info *thread; diff --git a/gdb/python/py-record.c b/gdb/python/py-record.c index 3c985c172e1..bf0551cfeac 100644 --- a/gdb/python/py-record.c +++ b/gdb/python/py-record.c @@ -55,10 +55,8 @@ PyTypeObject recpy_aux_type = { }; /* Python RecordGap object. */ -struct recpy_gap_object +struct recpy_gap_object: public PyObject { - PyObject_HEAD - /* Reason code. */ int reason_code; diff --git a/gdb/python/py-record.h b/gdb/python/py-record.h index 039a1b125e3..0722a918c68 100644 --- a/gdb/python/py-record.h +++ b/gdb/python/py-record.h @@ -25,10 +25,8 @@ #include "record.h" /* Python Record object. */ -struct recpy_record_object +struct recpy_record_object: public PyObject { - PyObject_HEAD - /* The thread this object refers to. */ thread_info *thread; @@ -39,10 +37,8 @@ struct recpy_record_object /* Python recorded element object. This is generic enough to represent recorded instructions as well as recorded function call segments, hence the generic name. */ -struct recpy_element_object +struct recpy_element_object: public PyObject { - PyObject_HEAD - /* The thread this object refers to. */ thread_info *thread; diff --git a/gdb/python/py-registers.c b/gdb/python/py-registers.c index 30b333c444c..d600b67be77 100644 --- a/gdb/python/py-registers.c +++ b/gdb/python/py-registers.c @@ -32,9 +32,8 @@ static const registry<gdbarch>::key<gdbpy_register_type> gdbpy_register_object_data; /* Structure for iterator over register descriptors. */ -struct register_descriptor_iterator_object { - PyObject_HEAD - +struct register_descriptor_iterator_object: public PyObject +{ /* The register group that the user is iterating over. This will never be NULL. */ const struct reggroup *reggroup; @@ -61,9 +60,8 @@ struct register_descriptor_object: public PyObject extern PyTypeObject register_descriptor_object_type; /* Structure for iterator over register groups. */ -struct reggroup_iterator_object { - PyObject_HEAD - +struct reggroup_iterator_object: public PyObject +{ /* The index into GROUPS for the next group to return. */ std::vector<const reggroup *>::size_type index; diff --git a/gdb/python/py-style.c b/gdb/python/py-style.c index f404864251c..3172400fcaf 100644 --- a/gdb/python/py-style.c +++ b/gdb/python/py-style.c @@ -36,10 +36,8 @@ static struct { }; /* A style. */ -struct style_object +struct style_object: public PyObject { - PyObject_HEAD - /* Underlying style, only valid when STYLE_NAME is NULL. */ ui_file_style style; diff --git a/gdb/python/py-type.c b/gdb/python/py-type.c index 1cc583b38d2..3b0525c6ec6 100644 --- a/gdb/python/py-type.c +++ b/gdb/python/py-type.c @@ -42,8 +42,8 @@ struct field_object: public gdbpy__dict__wrapper extern PyTypeObject field_object_type; /* A type iterator object. */ -struct typy_iterator_object { - PyObject_HEAD +struct typy_iterator_object: public PyObject +{ /* The current field index. */ int field; /* What to return. */ diff --git a/gdb/python/py-unwind.c b/gdb/python/py-unwind.c index f9fd848ea0e..07effe7fa88 100644 --- a/gdb/python/py-unwind.c +++ b/gdb/python/py-unwind.c @@ -66,10 +66,8 @@ show_pyuw_debug (struct ui_file *file, int from_tty, } \ } while (0) -struct pending_frame_object +struct pending_frame_object: public PyObject { - PyObject_HEAD - /* Frame we are unwinding. */ frame_info_ptr frame_info; @@ -94,10 +92,8 @@ struct saved_reg /* The data we keep for the PyUnwindInfo: pending_frame, saved registers and frame ID. */ -struct unwind_info_object +struct unwind_info_object: public PyObject { - PyObject_HEAD - /* gdb.PendingFrame for the frame we are unwinding. */ PyObject *pending_frame; diff --git a/gdb/python/py-value.c b/gdb/python/py-value.c index 6c28f13a3de..4f2f9cc4b0b 100644 --- a/gdb/python/py-value.c +++ b/gdb/python/py-value.c @@ -54,8 +54,8 @@ #define builtin_type_pybool \ language_bool_type (current_language, gdbpy_enter::get_gdbarch ()) -struct value_object { - PyObject_HEAD +struct value_object: public PyObject +{ struct value_object *next; struct value_object *prev; struct value *value; -- 2.52.0 ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2 6/6] gdb: make remaining Python extension objects inherit from PyObject 2026-01-27 17:02 ` [PATCH v2 6/6] gdb: make remaining Python extension objects inherit from PyObject Matthieu Longo @ 2026-01-27 18:29 ` Tom Tromey 2026-01-28 11:58 ` Matthieu Longo 0 siblings, 1 reply; 20+ messages in thread From: Tom Tromey @ 2026-01-27 18:29 UTC (permalink / raw) To: Matthieu Longo; +Cc: gdb-patches, Tom Tromey >>>>> "Matthieu" == Matthieu Longo <matthieu.longo@arm.com> writes: Matthieu> Previous patches made some Python extension objects ipublicly inherit Matthieu> directly or indirectly from PyObject. Matthieu> In the interest of consistency, this patch makes all remaining Python Matthieu> extension objects still not inheriting from PyObject do so. This is ok with the missing space issue fixed. Approved-By: Tom Tromey <tom@tromey.com> Tom ^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2 6/6] gdb: make remaining Python extension objects inherit from PyObject 2026-01-27 18:29 ` Tom Tromey @ 2026-01-28 11:58 ` Matthieu Longo 0 siblings, 0 replies; 20+ messages in thread From: Matthieu Longo @ 2026-01-28 11:58 UTC (permalink / raw) To: Tom Tromey; +Cc: gdb-patches On 27/01/2026 18:29, Tom Tromey wrote: >>>>>> "Matthieu" == Matthieu Longo <matthieu.longo@arm.com> writes: > > Matthieu> Previous patches made some Python extension objects ipublicly inherit > Matthieu> directly or indirectly from PyObject. > Matthieu> In the interest of consistency, this patch makes all remaining Python > Matthieu> extension objects still not inheriting from PyObject do so. > > This is ok with the missing space issue fixed. > Approved-By: Tom Tromey <tom@tromey.com> > > Tom Fixed. Matthieu ^ permalink raw reply [flat|nested] 20+ messages in thread
end of thread, other threads:[~2026-01-28 17:51 UTC | newest] Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-01-27 17:02 [PATCH v2 0/6] gdb: minor fixes for Python limited C API support Matthieu Longo 2026-01-27 17:02 ` [PATCH v2 1/6] Python limited API: migrate Py_CompileStringExFlags and PyRun_SimpleString Matthieu Longo 2026-01-27 17:54 ` Tom Tromey 2026-01-27 17:02 ` [PATCH v2 2/6] Python limited API: migrate PyImport_ExtendInittab Matthieu Longo 2026-01-27 17:54 ` Tom Tromey 2026-01-27 17:02 ` [PATCH v2 3/6] gdbpy_registry: cast C extension type object to PyObject * before Py_XINCREF Matthieu Longo 2026-01-27 18:01 ` Tom Tromey 2026-01-27 18:29 ` Tom Tromey 2026-01-28 11:58 ` Matthieu Longo 2026-01-27 17:02 ` [PATCH v2 4/6] gdb: new setters and getters for __dict__, and attributes Matthieu Longo 2026-01-27 19:06 ` Tom Tromey 2026-01-28 11:57 ` Matthieu Longo 2026-01-28 17:43 ` Matthieu Longo 2026-01-28 17:51 ` Tom Tromey 2026-01-27 17:02 ` [PATCH v2 5/6] gdb: cast all Python extension objects passed to gdbpy_ref_policy to PyObject* Matthieu Longo 2026-01-27 18:28 ` Tom Tromey 2026-01-28 11:58 ` Matthieu Longo 2026-01-27 17:02 ` [PATCH v2 6/6] gdb: make remaining Python extension objects inherit from PyObject Matthieu Longo 2026-01-27 18:29 ` Tom Tromey 2026-01-28 11:58 ` Matthieu Longo
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox