]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
gdb: new setters and getters for __dict__, and attributes
authorMatthieu Longo <matthieu.longo@arm.com>
Thu, 17 Jul 2025 17:36:41 +0000 (18:36 +0100)
committerMatthieu Longo <matthieu.longo@arm.com>
Thu, 29 Jan 2026 16:46:14 +0000 (16:46 +0000)
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>
gdb/python/py-corefile.c
gdb/python/py-event.c
gdb/python/py-event.h
gdb/python/py-inferior.c
gdb/python/py-infthread.c
gdb/python/py-objfile.c
gdb/python/py-progspace.c
gdb/python/py-ref.h
gdb/python/py-type.c
gdb/python/py-utils.c
gdb/python/python-internal.h

index 6847722628f72f2c75cd2393bade55d69ee856dd..24b573b2dbde61dae06efcca9269cd7450ecf126 100644 (file)
 
 /* 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 */
index c985159a6f7b4cac39482f01112f831ab465eb44..8fb21b7fdad7789723dc27ab737c559bda6d9652 100644 (file)
@@ -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 */
 };
index 6c81d64eb4fad12760a2a0f6ac5faf21a82c4e13..ec2e7bc03c582fdaf1beb9629a5fd0ea5df33686 100644 (file)
 #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);
index 8230f9d39433f3821784be5a3bbb5d95390bb924..69a7bfa3c37e4f4e10187f2d94938d6d01ae21bb 100644 (file)
 #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 */
 };
index e5d3222f9aeaa50573236c32c7b03571e4833ab4..d75742360d4f526c18e61ffd054e238756b25d28 100644 (file)
@@ -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 */
 };
index 8cf365a27dc0e98e247727041478e3a7fa807e5b..36acdb06a25c6275819ab54c7e68cb8b1db1ef56 100644 (file)
 #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 */
index ee26b761adb700833d43a46c39d72a11abc2e32e..19f5e533b0ac65416a29f9091d8a6b4f534c6fd9 100644 (file)
 #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 */
index b09d88dc30dc041356238258ca90b075c5350d84..4d44ca90e210e779a36283f93089866265a7749c 100644 (file)
@@ -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 */
index f39cb0240c8dcc59562b307bcb5554294bda0a91..76f001b99da4fdc71dba82bd13279b4a6ba97cc9 100644 (file)
@@ -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 */
index 131230f80b343dc1bc912edf641588477c84e9a3..8283b30db04843f497565839e6015c33ea3f5264 100644 (file)
@@ -309,24 +309,92 @@ 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 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);
 }
 
+\f
+
 /* Like PyModule_AddObject, but does not steal a reference to
    OBJECT.  */
 
index 95619bf775e0e1cbedd70100754734696a7d3540..f6915a62b7a57ce6268531c305ecf548e30ceb12 100644 (file)
@@ -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);