summaryrefslogtreecommitdiffstats
path: root/Objects
diff options
context:
space:
mode:
Diffstat (limited to 'Objects')
-rw-r--r--Objects/codeobject.c38
-rw-r--r--Objects/dictobject.c34
-rw-r--r--Objects/funcobject.c38
-rw-r--r--Objects/genobject.c2
4 files changed, 106 insertions, 6 deletions
diff --git a/Objects/codeobject.c b/Objects/codeobject.c
index 175bd57..65b1d25 100644
--- a/Objects/codeobject.c
+++ b/Objects/codeobject.c
@@ -11,9 +11,24 @@
#include "pycore_tuple.h" // _PyTuple_ITEMS()
#include "clinic/codeobject.c.h"
+static PyObject* code_repr(PyCodeObject *co);
+
+static const char *
+code_event_name(PyCodeEvent event) {
+ switch (event) {
+ #define CASE(op) \
+ case PY_CODE_EVENT_##op: \
+ return "PY_CODE_EVENT_" #op;
+ PY_FOREACH_CODE_EVENT(CASE)
+ #undef CASE
+ }
+ Py_UNREACHABLE();
+}
+
static void
notify_code_watchers(PyCodeEvent event, PyCodeObject *co)
{
+ assert(Py_REFCNT(co) > 0);
PyInterpreterState *interp = _PyInterpreterState_GET();
assert(interp->_initialized);
uint8_t bits = interp->active_code_watchers;
@@ -25,7 +40,21 @@ notify_code_watchers(PyCodeEvent event, PyCodeObject *co)
// callback must be non-null if the watcher bit is set
assert(cb != NULL);
if (cb(event, co) < 0) {
- PyErr_WriteUnraisable((PyObject *) co);
+ // Don't risk resurrecting the object if an unraisablehook keeps
+ // a reference; pass a string as context.
+ PyObject *context = NULL;
+ PyObject *repr = code_repr(co);
+ if (repr) {
+ context = PyUnicode_FromFormat(
+ "%s watcher callback for %U",
+ code_event_name(event), repr);
+ Py_DECREF(repr);
+ }
+ if (context == NULL) {
+ context = Py_NewRef(Py_None);
+ }
+ PyErr_WriteUnraisable(context);
+ Py_DECREF(context);
}
}
i++;
@@ -1667,7 +1696,14 @@ code_new_impl(PyTypeObject *type, int argcount, int posonlyargcount,
static void
code_dealloc(PyCodeObject *co)
{
+ assert(Py_REFCNT(co) == 0);
+ Py_SET_REFCNT(co, 1);
notify_code_watchers(PY_CODE_EVENT_DESTROY, co);
+ if (Py_REFCNT(co) > 1) {
+ Py_SET_REFCNT(co, Py_REFCNT(co) - 1);
+ return;
+ }
+ Py_SET_REFCNT(co, 0);
if (co->co_extra != NULL) {
PyInterpreterState *interp = _PyInterpreterState_GET();
diff --git a/Objects/dictobject.c b/Objects/dictobject.c
index fc658ca..e3795e7 100644
--- a/Objects/dictobject.c
+++ b/Objects/dictobject.c
@@ -2308,7 +2308,14 @@ Fail:
static void
dict_dealloc(PyDictObject *mp)
{
+ assert(Py_REFCNT(mp) == 0);
+ Py_SET_REFCNT(mp, 1);
_PyDict_NotifyEvent(PyDict_EVENT_DEALLOCATED, mp, NULL, NULL);
+ if (Py_REFCNT(mp) > 1) {
+ Py_SET_REFCNT(mp, Py_REFCNT(mp) - 1);
+ return;
+ }
+ Py_SET_REFCNT(mp, 0);
PyDictValues *values = mp->ma_values;
PyDictKeysObject *keys = mp->ma_keys;
Py_ssize_t i, n;
@@ -5732,6 +5739,18 @@ PyDict_ClearWatcher(int watcher_id)
return 0;
}
+static const char *
+dict_event_name(PyDict_WatchEvent event) {
+ switch (event) {
+ #define CASE(op) \
+ case PyDict_EVENT_##op: \
+ return "PyDict_EVENT_" #op;
+ PY_FOREACH_DICT_EVENT(CASE)
+ #undef CASE
+ }
+ Py_UNREACHABLE();
+}
+
void
_PyDict_SendEvent(int watcher_bits,
PyDict_WatchEvent event,
@@ -5744,9 +5763,18 @@ _PyDict_SendEvent(int watcher_bits,
if (watcher_bits & 1) {
PyDict_WatchCallback cb = interp->dict_state.watchers[i];
if (cb && (cb(event, (PyObject*)mp, key, value) < 0)) {
- // some dict modification paths (e.g. PyDict_Clear) can't raise, so we
- // can't propagate exceptions from dict watchers.
- PyErr_WriteUnraisable((PyObject *)mp);
+ // We don't want to resurrect the dict by potentially having an
+ // unraisablehook keep a reference to it, so we don't pass the
+ // dict as context, just an informative string message. Dict
+ // repr can call arbitrary code, so we invent a simpler version.
+ PyObject *context = PyUnicode_FromFormat(
+ "%s watcher callback for <dict at %p>",
+ dict_event_name(event), mp);
+ if (context == NULL) {
+ context = Py_NewRef(Py_None);
+ }
+ PyErr_WriteUnraisable(context);
+ Py_DECREF(context);
}
}
watcher_bits >>= 1;
diff --git a/Objects/funcobject.c b/Objects/funcobject.c
index 91a6b3d..99048ea 100644
--- a/Objects/funcobject.c
+++ b/Objects/funcobject.c
@@ -8,6 +8,20 @@
#include "pycore_pyerrors.h" // _PyErr_Occurred()
#include "structmember.h" // PyMemberDef
+static PyObject* func_repr(PyFunctionObject *op);
+
+static const char *
+func_event_name(PyFunction_WatchEvent event) {
+ switch (event) {
+ #define CASE(op) \
+ case PyFunction_EVENT_##op: \
+ return "PyFunction_EVENT_" #op;
+ PY_FOREACH_FUNC_EVENT(CASE)
+ #undef CASE
+ }
+ Py_UNREACHABLE();
+}
+
static void
notify_func_watchers(PyInterpreterState *interp, PyFunction_WatchEvent event,
PyFunctionObject *func, PyObject *new_value)
@@ -21,7 +35,21 @@ notify_func_watchers(PyInterpreterState *interp, PyFunction_WatchEvent event,
// callback must be non-null if the watcher bit is set
assert(cb != NULL);
if (cb(event, func, new_value) < 0) {
- PyErr_WriteUnraisable((PyObject *) func);
+ // Don't risk resurrecting the func if an unraisablehook keeps a
+ // reference; pass a string as context.
+ PyObject *context = NULL;
+ PyObject *repr = func_repr(func);
+ if (repr != NULL) {
+ context = PyUnicode_FromFormat(
+ "%s watcher callback for %U",
+ func_event_name(event), repr);
+ Py_DECREF(repr);
+ }
+ if (context == NULL) {
+ context = Py_NewRef(Py_None);
+ }
+ PyErr_WriteUnraisable(context);
+ Py_DECREF(context);
}
}
i++;
@@ -33,6 +61,7 @@ static inline void
handle_func_event(PyFunction_WatchEvent event, PyFunctionObject *func,
PyObject *new_value)
{
+ assert(Py_REFCNT(func) > 0);
PyInterpreterState *interp = _PyInterpreterState_GET();
assert(interp->_initialized);
if (interp->active_func_watchers) {
@@ -766,7 +795,14 @@ func_clear(PyFunctionObject *op)
static void
func_dealloc(PyFunctionObject *op)
{
+ assert(Py_REFCNT(op) == 0);
+ Py_SET_REFCNT(op, 1);
handle_func_event(PyFunction_EVENT_DESTROY, op, NULL);
+ if (Py_REFCNT(op) > 1) {
+ Py_SET_REFCNT(op, Py_REFCNT(op) - 1);
+ return;
+ }
+ Py_SET_REFCNT(op, 0);
_PyObject_GC_UNTRACK(op);
if (op->func_weakreflist != NULL) {
PyObject_ClearWeakRefs((PyObject *) op);
diff --git a/Objects/genobject.c b/Objects/genobject.c
index 4ab6581..be08a59 100644
--- a/Objects/genobject.c
+++ b/Objects/genobject.c
@@ -1549,7 +1549,7 @@ ag_getframe(PyAsyncGenObject *ag, void *Py_UNUSED(ignored))
static PyObject *
ag_getcode(PyGenObject *gen, void *Py_UNUSED(ignored))
{
- return _gen_getcode(gen, "ag__code");
+ return _gen_getcode(gen, "ag_code");
}
static PyGetSetDef async_gen_getsetlist[] = {