binutils-gdb/gdb/python/py-event.h
Matthieu Longo 9a84753aa7 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>
2026-01-29 16:46:14 +00:00

82 lines
2.8 KiB
C

/* Python interface to inferior events.
Copyright (C) 2009-2026 Free Software Foundation, Inc.
This file is part of GDB.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#ifndef GDB_PYTHON_PY_EVENT_H
#define GDB_PYTHON_PY_EVENT_H
#include "py-events.h"
#include "command.h"
#include "python-internal.h"
#include "inferior.h"
/* Declare all event types. */
#define GDB_PY_DEFINE_EVENT_TYPE(name, py_name, doc, base) \
extern PyTypeObject name##_event_object_type;
#include "py-event-types.def"
#undef GDB_PY_DEFINE_EVENT_TYPE
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);
/* For inferior function call events, discriminate whether event is
before or after the call. */
enum inferior_call_kind
{
/* Before the call */
INFERIOR_CALL_PRE,
/* after the call */
INFERIOR_CALL_POST,
};
extern int emit_inferior_call_event (inferior_call_kind kind,
ptid_t thread, CORE_ADDR addr);
extern int emit_register_changed_event (const frame_info_ptr &frame,
int regnum);
extern int emit_memory_changed_event (CORE_ADDR addr, ssize_t len);
extern int evpy_emit_event (PyObject *event,
eventregistry_object *registry);
/* Emits a thread exit event for THREAD */
extern int emit_thread_exit_event (thread_info * thread);
extern gdbpy_ref<> create_event_object (PyTypeObject *py_type);
/* thread events can either be thread specific or process wide. If gdb is
running in non-stop mode then the event is thread specific, otherwise
it is process wide.
This function returns the currently stopped thread in non-stop mode and
Py_None otherwise. */
extern gdbpy_ref<> py_get_event_thread (ptid_t ptid);
extern gdbpy_ref<> create_thread_event_object (PyTypeObject *py_type,
PyObject *thread);
extern int emit_new_objfile_event (struct objfile *objfile);
extern int emit_free_objfile_event (struct objfile *objfile);
extern int emit_clear_objfiles_event (program_space *pspace);
extern void evpy_dealloc (PyObject *self);
extern int evpy_add_attribute (PyObject *event,
const char *name, PyObject *attr);
#endif /* GDB_PYTHON_PY_EVENT_H */