aboutsummaryrefslogtreecommitdiff
path: root/gdb/python
diff options
context:
space:
mode:
authorMatthieu Longo <matthieu.longo@arm.com>2025-07-17 18:36:41 +0100
committerMatthieu Longo <matthieu.longo@arm.com>2026-01-29 16:46:14 +0000
commit9a84753aa7f8b8939cf4eea9c7f1db4b42e171e1 (patch)
tree027a0af3564eff86d5d6800a436d757945e91cec /gdb/python
parent8b0f0d5fbf8d870c433dc62ceaaf740740af9923 (diff)
downloadbinutils-9a84753aa7f8b8939cf4eea9c7f1db4b42e171e1.tar.gz
binutils-9a84753aa7f8b8939cf4eea9c7f1db4b42e171e1.tar.bz2
binutils-9a84753aa7f8b8939cf4eea9c7f1db4b42e171e1.zip
gdb: new setters and getters for __dict__, and attributes
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_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 Approved-By: Tom Tromey <tom@tromey.com>
Diffstat (limited to 'gdb/python')
-rw-r--r--gdb/python/py-corefile.c18
-rw-r--r--gdb/python/py-event.c10
-rw-r--r--gdb/python/py-event.h8
-rw-r--r--gdb/python/py-inferior.c29
-rw-r--r--gdb/python/py-infthread.c10
-rw-r--r--gdb/python/py-objfile.c18
-rw-r--r--gdb/python/py-progspace.c18
-rw-r--r--gdb/python/py-ref.h38
-rw-r--r--gdb/python/py-type.c19
-rw-r--r--gdb/python/py-utils.c90
-rw-r--r--gdb/python/python-internal.h34
11 files changed, 184 insertions, 108 deletions
diff --git a/gdb/python/py-corefile.c b/gdb/python/py-corefile.c
index 6847722628f..24b573b2dbd 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..ec2e7bc03c5 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..36acdb06a25 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..19f5e533b0a 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..4d44ca90e21 100644
--- a/gdb/python/py-ref.h
+++ b/gdb/python/py-ref.h
@@ -42,4 +42,42 @@ 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. */
+ static PyObject **compute_addr (PyObject *self)
+ {
+ 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 f39cb0240c8..76f001b99da 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..8283b30db04 100644
--- a/gdb/python/py-utils.c
+++ b/gdb/python/py-utils.c
@@ -309,24 +309,92 @@ gdb_py_int_as_long (PyObject *obj, long *result)
-/* 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 ATTRIBUTE_UNUSED)
{
- PyObject *result;
- PyTypeObject *type_obj = (PyTypeObject *) closure;
- char *raw_ptr;
+ PyObject **py_dict_ptr = gdbpy_dict_wrapper::compute_addr (self);
+ 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 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;
+
+ gdbpy_ref<> dict (gdb_py_generic_dict_getter (self, nullptr));
+ if (dict == nullptr)
+ return nullptr;
+
+ /* Clear previous AttributeError set by PyObject_GenericGetAttr when it
+ did not find the attribute, and try to get the attribute from __dict__. */
+ PyErr_Clear();
+
+ value = PyDict_GetItemWithError (dict.get (), attr);
+ if (value != nullptr)
+ return Py_NewRef (value);
+
+ /* If PyDict_GetItemWithError() returns NULL because an error occurred, it
+ sets an exception. Propagate it by returning NULL. */
+ if (PyErr_Occurred () != nullptr)
+ return nullptr;
+
+ /* If the key is not found, PyDict_GetItemWithError() returns NULL without
+ setting an exception. Failing to set one here would later result in:
+ <class 'SystemError'>: error return without exception set
+ Therefore, we must explicitly raise an AttributeError in this case. */
+ PyErr_Format (PyExc_AttributeError,
+ "'%s' object has no attribute '%s'",
+ Py_TYPE (self)->tp_name,
+ PyUnicode_AsUTF8AndSize (attr, nullptr));
+ return nullptr;
+}
- Py_INCREF (result);
- return result;
+/* 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;
+
+ gdbpy_ref<> dict (gdb_py_generic_dict_getter (self, nullptr));
+ if (dict == nullptr)
+ return -1;
+
+ /* Clear previous AttributeError set by PyObject_GenericGetAttr() when it
+ did not find the attribute, and try to set the attribute into __dict__. */
+ PyErr_Clear();
+
+ /* Set the new value.
+ Note: the old value is managed by PyDict_SetItem(), so no need to get
+ a borrowed reference on it and decrement its reference counter before
+ setting a new value. */
+ return PyDict_SetItem (dict.get (), attr, value);
}
+
+
/* 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..f6915a62b7a 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,9 @@ 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);
+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);