GH-91054: Add code object watchers API (GH-99859)

* Add API to allow extensions to set callback function on creation and destruction of PyCodeObject

Co-authored-by: Ye11ow-Flash <janshah@cs.stonybrook.edu>
This commit is contained in:
Itamar Ostricher 2022-12-02 09:28:27 -08:00 committed by GitHub
parent 0563be23a5
commit 3c137dc613
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 364 additions and 0 deletions

View file

@ -12,6 +12,66 @@
#include "clinic/codeobject.c.h"
static void
notify_code_watchers(PyCodeEvent event, PyCodeObject *co)
{
PyInterpreterState *interp = _PyInterpreterState_GET();
if (interp->active_code_watchers) {
assert(interp->_initialized);
for (int i = 0; i < CODE_MAX_WATCHERS; i++) {
PyCode_WatchCallback cb = interp->code_watchers[i];
if ((cb != NULL) && (cb(event, co) < 0)) {
PyErr_WriteUnraisable((PyObject *) co);
}
}
}
}
int
PyCode_AddWatcher(PyCode_WatchCallback callback)
{
PyInterpreterState *interp = _PyInterpreterState_GET();
assert(interp->_initialized);
for (int i = 0; i < CODE_MAX_WATCHERS; i++) {
if (!interp->code_watchers[i]) {
interp->code_watchers[i] = callback;
interp->active_code_watchers |= (1 << i);
return i;
}
}
PyErr_SetString(PyExc_RuntimeError, "no more code watcher IDs available");
return -1;
}
static inline int
validate_watcher_id(PyInterpreterState *interp, int watcher_id)
{
if (watcher_id < 0 || watcher_id >= CODE_MAX_WATCHERS) {
PyErr_Format(PyExc_ValueError, "Invalid code watcher ID %d", watcher_id);
return -1;
}
if (!interp->code_watchers[watcher_id]) {
PyErr_Format(PyExc_ValueError, "No code watcher set for ID %d", watcher_id);
return -1;
}
return 0;
}
int
PyCode_ClearWatcher(int watcher_id)
{
PyInterpreterState *interp = _PyInterpreterState_GET();
assert(interp->_initialized);
if (validate_watcher_id(interp, watcher_id) < 0) {
return -1;
}
interp->code_watchers[watcher_id] = NULL;
interp->active_code_watchers &= ~(1 << watcher_id);
return 0;
}
/******************
* generic helpers
******************/
@ -355,6 +415,7 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con)
}
co->_co_firsttraceable = entry_point;
_PyCode_Quicken(co);
notify_code_watchers(PY_CODE_EVENT_CREATE, co);
}
static int
@ -1615,6 +1676,8 @@ code_new_impl(PyTypeObject *type, int argcount, int posonlyargcount,
static void
code_dealloc(PyCodeObject *co)
{
notify_code_watchers(PY_CODE_EVENT_DESTROY, co);
if (co->co_extra != NULL) {
PyInterpreterState *interp = _PyInterpreterState_GET();
_PyCodeObjectExtra *co_extra = co->co_extra;