diff options
author | shadchin <shadchin@yandex-team.com> | 2024-02-12 07:53:52 +0300 |
---|---|---|
committer | shadchin <shadchin@yandex-team.com> | 2024-02-12 08:07:36 +0300 |
commit | ce1b7ca3171f9158180640c6a02a74b4afffedea (patch) | |
tree | e47c1e8391b1b0128262c1e9b1e6ed4c8fff2348 /contrib/tools/python3/src/Objects/object.c | |
parent | 57350d96f030db90f220ce50ee591d5c5d403df7 (diff) | |
download | ydb-ce1b7ca3171f9158180640c6a02a74b4afffedea.tar.gz |
Update Python from 3.11.8 to 3.12.2
Diffstat (limited to 'contrib/tools/python3/src/Objects/object.c')
-rw-r--r-- | contrib/tools/python3/src/Objects/object.c | 749 |
1 files changed, 492 insertions, 257 deletions
diff --git a/contrib/tools/python3/src/Objects/object.c b/contrib/tools/python3/src/Objects/object.c index c4f2786c50..aac707d6a2 100644 --- a/contrib/tools/python3/src/Objects/object.c +++ b/contrib/tools/python3/src/Objects/object.c @@ -14,9 +14,10 @@ #include "pycore_pymem.h" // _PyMem_IsPtrFreed() #include "pycore_pystate.h" // _PyThreadState_GET() #include "pycore_symtable.h" // PySTEntry_Type -#include "pycore_typeobject.h" // _PyTypes_InitSlotDefs() +#include "pycore_typevarobject.h" // _PyTypeAlias_Type, _Py_initialize_generic +#include "pycore_typeobject.h" // _PyBufferWrapper_Type #include "pycore_unionobject.h" // _PyUnion_Type -#include "pycore_interpreteridobject.h" // _PyInterpreterID_Type +#include "interpreteridobject.h" // _PyInterpreterID_Type #ifdef Py_LIMITED_API // Prevent recursive call _Py_IncRef() <=> Py_INCREF() @@ -55,19 +56,100 @@ _PyObject_CheckConsistency(PyObject *op, int check_content) #ifdef Py_REF_DEBUG +/* We keep the legacy symbol around for backward compatibility. */ Py_ssize_t _Py_RefTotal; -Py_ssize_t -_Py_GetRefTotal(void) +static inline Py_ssize_t +get_legacy_reftotal(void) { return _Py_RefTotal; } +#endif + +#ifdef Py_REF_DEBUG + +# define REFTOTAL(interp) \ + interp->object_state.reftotal + +static inline void +reftotal_increment(PyInterpreterState *interp) +{ + REFTOTAL(interp)++; +} + +static inline void +reftotal_decrement(PyInterpreterState *interp) +{ + REFTOTAL(interp)--; +} + +static inline void +reftotal_add(PyInterpreterState *interp, Py_ssize_t n) +{ + REFTOTAL(interp) += n; +} + +static inline Py_ssize_t get_global_reftotal(_PyRuntimeState *); + +/* We preserve the number of refs leaked during runtime finalization, + so they can be reported if the runtime is initialized again. */ +// XXX We don't lose any information by dropping this, +// so we should consider doing so. +static Py_ssize_t last_final_reftotal = 0; + +void +_Py_FinalizeRefTotal(_PyRuntimeState *runtime) +{ + last_final_reftotal = get_global_reftotal(runtime); + runtime->object_state.interpreter_leaks = 0; +} + +void +_PyInterpreterState_FinalizeRefTotal(PyInterpreterState *interp) +{ + interp->runtime->object_state.interpreter_leaks += REFTOTAL(interp); + REFTOTAL(interp) = 0; +} + +static inline Py_ssize_t +get_reftotal(PyInterpreterState *interp) +{ + /* For a single interpreter, we ignore the legacy _Py_RefTotal, + since we can't determine which interpreter updated it. */ + return REFTOTAL(interp); +} + +static inline Py_ssize_t +get_global_reftotal(_PyRuntimeState *runtime) +{ + Py_ssize_t total = 0; + + /* Add up the total from each interpreter. */ + HEAD_LOCK(&_PyRuntime); + PyInterpreterState *interp = PyInterpreterState_Head(); + for (; interp != NULL; interp = PyInterpreterState_Next(interp)) { + total += REFTOTAL(interp); + } + HEAD_UNLOCK(&_PyRuntime); + + /* Add in the updated value from the legacy _Py_RefTotal. */ + total += get_legacy_reftotal(); + total += last_final_reftotal; + total += runtime->object_state.interpreter_leaks; + + return total; +} + +#undef REFTOTAL void _PyDebug_PrintTotalRefs(void) { + _PyRuntimeState *runtime = &_PyRuntime; fprintf(stderr, "[%zd refs, %zd blocks]\n", - _Py_GetRefTotal(), _Py_GetAllocatedBlocks()); + get_global_reftotal(runtime), _Py_GetGlobalAllocatedBlocks()); + /* It may be helpful to also print the "legacy" reftotal separately. + Likewise for the total for each interpreter. */ } #endif /* Py_REF_DEBUG */ @@ -76,11 +158,16 @@ _PyDebug_PrintTotalRefs(void) { Do not call them otherwise, they do not initialize the object! */ #ifdef Py_TRACE_REFS -/* Head of circular doubly-linked list of all objects. These are linked - * together via the _ob_prev and _ob_next members of a PyObject, which - * exist only in a Py_TRACE_REFS build. - */ -static PyObject refchain = {&refchain, &refchain}; + +#define REFCHAIN(interp) &interp->object_state.refchain + +static inline void +init_refchain(PyInterpreterState *interp) +{ + PyObject *refchain = REFCHAIN(interp); + refchain->_ob_prev = refchain; + refchain->_ob_next = refchain; +} /* Insert op at the front of the list of all objects. If force is true, * op is added even if _ob_prev and _ob_next are non-NULL already. If @@ -105,10 +192,11 @@ _Py_AddToAllObjects(PyObject *op, int force) } #endif if (force || op->_ob_prev == NULL) { - op->_ob_next = refchain._ob_next; - op->_ob_prev = &refchain; - refchain._ob_next->_ob_prev = op; - refchain._ob_next = op; + PyObject *refchain = REFCHAIN(_PyInterpreterState_GET()); + op->_ob_next = refchain->_ob_next; + op->_ob_prev = refchain; + refchain->_ob_next->_ob_prev = op; + refchain->_ob_next = op; } } #endif /* Py_TRACE_REFS */ @@ -122,6 +210,58 @@ _Py_NegativeRefcount(const char *filename, int lineno, PyObject *op) filename, lineno, __func__); } +/* This is used strictly by Py_INCREF(). */ +void +_Py_INCREF_IncRefTotal(void) +{ + reftotal_increment(_PyInterpreterState_GET()); +} + +/* This is used strictly by Py_DECREF(). */ +void +_Py_DECREF_DecRefTotal(void) +{ + reftotal_decrement(_PyInterpreterState_GET()); +} + +void +_Py_IncRefTotal(PyInterpreterState *interp) +{ + reftotal_increment(interp); +} + +void +_Py_DecRefTotal(PyInterpreterState *interp) +{ + reftotal_decrement(interp); +} + +void +_Py_AddRefTotal(PyInterpreterState *interp, Py_ssize_t n) +{ + reftotal_add(interp, n); +} + +/* This includes the legacy total + and any carried over from the last runtime init/fini cycle. */ +Py_ssize_t +_Py_GetGlobalRefTotal(void) +{ + return get_global_reftotal(&_PyRuntime); +} + +Py_ssize_t +_Py_GetLegacyRefTotal(void) +{ + return get_legacy_reftotal(); +} + +Py_ssize_t +_PyInterpreterState_GetRefTotal(PyInterpreterState *interp) +{ + return get_reftotal(interp); +} + #endif /* Py_REF_DEBUG */ void @@ -148,6 +288,9 @@ _Py_DecRef(PyObject *o) Py_DECREF(o); } + +/**************************************/ + PyObject * PyObject_Init(PyObject *op, PyTypeObject *tp) { @@ -239,17 +382,12 @@ PyObject_CallFinalizerFromDealloc(PyObject *self) /* tp_finalize resurrected it! Make it look like the original Py_DECREF * never happened. */ Py_ssize_t refcnt = Py_REFCNT(self); - _Py_NewReference(self); + _Py_NewReferenceNoTotal(self); Py_SET_REFCNT(self, refcnt); _PyObject_ASSERT(self, (!_PyType_IS_GC(Py_TYPE(self)) || _PyObject_GC_IS_TRACKED(self))); - /* If Py_REF_DEBUG macro is defined, _Py_NewReference() increased - _Py_RefTotal, so we need to undo that. */ -#ifdef Py_REF_DEBUG - _Py_RefTotal--; -#endif return -1; } @@ -273,11 +411,8 @@ PyObject_Print(PyObject *op, FILE *fp, int flags) } else { if (Py_REFCNT(op) <= 0) { - /* XXX(twouters) cast refcount to long until %zd is - universally available */ Py_BEGIN_ALLOW_THREADS - fprintf(fp, "<refcnt %ld at %p>", - (long)Py_REFCNT(op), (void *)op); + fprintf(fp, "<refcnt %zd at %p>", Py_REFCNT(op), (void *)op); Py_END_ALLOW_THREADS } else { @@ -286,31 +421,22 @@ PyObject_Print(PyObject *op, FILE *fp, int flags) s = PyObject_Str(op); else s = PyObject_Repr(op); - if (s == NULL) + if (s == NULL) { ret = -1; - else if (PyBytes_Check(s)) { - fwrite(PyBytes_AS_STRING(s), 1, - PyBytes_GET_SIZE(s), fp); } - else if (PyUnicode_Check(s)) { - PyObject *t; - t = PyUnicode_AsEncodedString(s, "utf-8", "backslashreplace"); + else { + assert(PyUnicode_Check(s)); + const char *t; + Py_ssize_t len; + t = PyUnicode_AsUTF8AndSize(s, &len); if (t == NULL) { ret = -1; } else { - fwrite(PyBytes_AS_STRING(t), 1, - PyBytes_GET_SIZE(t), fp); - Py_DECREF(t); + fwrite(t, 1, len, fp); } + Py_DECREF(s); } - else { - PyErr_Format(PyExc_TypeError, - "str() or repr() returned '%.100s'", - Py_TYPE(s)->tp_name); - ret = -1; - } - Py_XDECREF(s); } } if (ret == 0) { @@ -370,9 +496,7 @@ _PyObject_Dump(PyObject* op) /* first, write fields which are the least likely to crash */ fprintf(stderr, "object address : %p\n", (void *)op); - /* XXX(twouters) cast refcount to long until %zd is - universally available */ - fprintf(stderr, "object refcount : %ld\n", (long)Py_REFCNT(op)); + fprintf(stderr, "object refcount : %zd\n", Py_REFCNT(op)); fflush(stderr); PyTypeObject *type = Py_TYPE(op); @@ -385,13 +509,12 @@ _PyObject_Dump(PyObject* op) fflush(stderr); PyGILState_STATE gil = PyGILState_Ensure(); - PyObject *error_type, *error_value, *error_traceback; - PyErr_Fetch(&error_type, &error_value, &error_traceback); + PyObject *exc = PyErr_GetRaisedException(); (void)PyObject_Print(op, stderr, 0); fflush(stderr); - PyErr_Restore(error_type, error_value, error_traceback); + PyErr_SetRaisedException(exc); PyGILState_Release(gil); fprintf(stderr, "\n"); @@ -470,8 +593,7 @@ PyObject_Str(PyObject *v) if (PyUnicode_READY(v) < 0) return NULL; #endif - Py_INCREF(v); - return v; + return Py_NewRef(v); } if (Py_TYPE(v)->tp_str == NULL) return PyObject_Repr(v); @@ -547,8 +669,7 @@ PyObject_Bytes(PyObject *v) return PyBytes_FromString("<NULL>"); if (PyBytes_CheckExact(v)) { - Py_INCREF(v); - return v; + return Py_NewRef(v); } func = _PyObject_LookupSpecial(v, &_Py_ID(__bytes__)); @@ -704,8 +825,7 @@ do_richcompare(PyThreadState *tstate, PyObject *v, PyObject *w, int op) Py_TYPE(w)->tp_name); return NULL; } - Py_INCREF(res); - return res; + return Py_NewRef(res); } /* Perform a rich comparison with object result. This wraps do_richcompare() @@ -778,7 +898,7 @@ PyObject_Hash(PyObject *v) * an explicit call to PyType_Ready, we implicitly call * PyType_Ready here and then check the tp_hash slot again */ - if (tp->tp_dict == NULL) { + if (!_PyType_IsReady(tp)) { if (PyType_Ready(tp) < 0) return -1; if (tp->tp_hash != NULL) @@ -806,13 +926,24 @@ PyObject_GetAttrString(PyObject *v, const char *name) int PyObject_HasAttrString(PyObject *v, const char *name) { - PyObject *res = PyObject_GetAttrString(v, name); - if (res != NULL) { - Py_DECREF(res); - return 1; + if (Py_TYPE(v)->tp_getattr != NULL) { + PyObject *res = (*Py_TYPE(v)->tp_getattr)(v, (char*)name); + if (res != NULL) { + Py_DECREF(res); + return 1; + } + PyErr_Clear(); + return 0; } - PyErr_Clear(); - return 0; + + PyObject *attr_name = PyUnicode_FromString(name); + if (attr_name == NULL) { + PyErr_Clear(); + return 0; + } + int ok = PyObject_HasAttr(v, attr_name); + Py_DECREF(attr_name); + return ok; } int @@ -878,25 +1009,22 @@ set_attribute_error_context(PyObject* v, PyObject* name) return 0; } // Intercept AttributeError exceptions and augment them to offer suggestions later. - PyObject *type, *value, *traceback; - PyErr_Fetch(&type, &value, &traceback); - PyErr_NormalizeException(&type, &value, &traceback); - // Check if the normalized exception is indeed an AttributeError - if (!PyErr_GivenExceptionMatches(value, PyExc_AttributeError)) { + PyObject *exc = PyErr_GetRaisedException(); + if (!PyErr_GivenExceptionMatches(exc, PyExc_AttributeError)) { goto restore; } - PyAttributeErrorObject* the_exc = (PyAttributeErrorObject*) value; + PyAttributeErrorObject* the_exc = (PyAttributeErrorObject*) exc; // Check if this exception was already augmented if (the_exc->name || the_exc->obj) { goto restore; } // Augment the exception with the name and object - if (PyObject_SetAttr(value, &_Py_ID(name), name) || - PyObject_SetAttr(value, &_Py_ID(obj), v)) { + if (PyObject_SetAttr(exc, &_Py_ID(name), name) || + PyObject_SetAttr(exc, &_Py_ID(obj), v)) { return 1; } restore: - PyErr_Restore(type, value, traceback); + PyErr_SetRaisedException(exc); return 0; } @@ -924,7 +1052,7 @@ PyObject_GetAttr(PyObject *v, PyObject *name) } else { PyErr_Format(PyExc_AttributeError, - "'%.50s' object has no attribute '%U'", + "'%.100s' object has no attribute '%U'", tp->tp_name, name); } @@ -957,7 +1085,26 @@ _PyObject_LookupAttr(PyObject *v, PyObject *name, PyObject **result) } return 0; } - if (tp->tp_getattro != NULL) { + if (tp->tp_getattro == (getattrofunc)_Py_type_getattro) { + int supress_missing_attribute_exception = 0; + *result = _Py_type_getattro_impl((PyTypeObject*)v, name, &supress_missing_attribute_exception); + if (supress_missing_attribute_exception) { + // return 0 without having to clear the exception + return 0; + } + } + else if (tp->tp_getattro == (getattrofunc)_Py_module_getattro) { + // optimization: suppress attribute error from module getattro method + *result = _Py_module_getattro_impl((PyModuleObject*)v, name, 1); + if (*result != NULL) { + return 1; + } + if (PyErr_Occurred()) { + return -1; + } + return 0; + } + else if (tp->tp_getattro != NULL) { *result = (*tp->tp_getattro)(v, name); } else if (tp->tp_getattr != NULL) { @@ -1059,18 +1206,19 @@ PyObject_SetAttr(PyObject *v, PyObject *name, PyObject *value) } PyObject ** -_PyObject_DictPointer(PyObject *obj) +_PyObject_ComputedDictPointer(PyObject *obj) { - Py_ssize_t dictoffset; PyTypeObject *tp = Py_TYPE(obj); + assert((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0); - if (tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) { - return _PyObject_ManagedDictPointer(obj); - } - dictoffset = tp->tp_dictoffset; - if (dictoffset == 0) + Py_ssize_t dictoffset = tp->tp_dictoffset; + if (dictoffset == 0) { return NULL; + } + if (dictoffset < 0) { + assert(dictoffset != -1); + Py_ssize_t tsize = Py_SIZE(obj); if (tsize < 0) { tsize = -tsize; @@ -1096,29 +1244,24 @@ PyObject ** _PyObject_GetDictPtr(PyObject *obj) { if ((Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) { - return _PyObject_DictPointer(obj); + return _PyObject_ComputedDictPointer(obj); } - PyObject **dict_ptr = _PyObject_ManagedDictPointer(obj); - PyDictValues **values_ptr = _PyObject_ValuesPointer(obj); - if (*values_ptr == NULL) { - return dict_ptr; - } - assert(*dict_ptr == NULL); - PyObject *dict = _PyObject_MakeDictFromInstanceAttributes(obj, *values_ptr); - if (dict == NULL) { - PyErr_Clear(); - return NULL; + PyDictOrValues *dorv_ptr = _PyObject_DictOrValuesPointer(obj); + if (_PyDictOrValues_IsValues(*dorv_ptr)) { + PyObject *dict = _PyObject_MakeDictFromInstanceAttributes(obj, _PyDictOrValues_GetValues(*dorv_ptr)); + if (dict == NULL) { + PyErr_Clear(); + return NULL; + } + dorv_ptr->dict = dict; } - *values_ptr = NULL; - *dict_ptr = dict; - return dict_ptr; + return &dorv_ptr->dict; } PyObject * PyObject_SelfIter(PyObject *obj) { - Py_INCREF(obj); - return obj; + return Py_NewRef(obj); } /* Helper used when the __next__ method is removed from a type: @@ -1180,36 +1323,46 @@ _PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method) } } } - PyDictValues *values; - if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) && - (values = *_PyObject_ValuesPointer(obj))) - { - assert(*_PyObject_DictPointer(obj) == NULL); - PyObject *attr = _PyObject_GetInstanceAttribute(obj, values, name); - if (attr != NULL) { - *method = attr; - Py_XDECREF(descr); - return 0; - } - } - else { - PyObject **dictptr = _PyObject_DictPointer(obj); - PyObject *dict; - if (dictptr != NULL && (dict = *dictptr) != NULL) { - Py_INCREF(dict); - PyObject *attr = PyDict_GetItemWithError(dict, name); + PyObject *dict; + if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT)) { + PyDictOrValues* dorv_ptr = _PyObject_DictOrValuesPointer(obj); + if (_PyDictOrValues_IsValues(*dorv_ptr)) { + PyDictValues *values = _PyDictOrValues_GetValues(*dorv_ptr); + PyObject *attr = _PyObject_GetInstanceAttribute(obj, values, name); if (attr != NULL) { - *method = Py_NewRef(attr); - Py_DECREF(dict); + *method = attr; Py_XDECREF(descr); return 0; } + dict = NULL; + } + else { + dict = dorv_ptr->dict; + } + } + else { + PyObject **dictptr = _PyObject_ComputedDictPointer(obj); + if (dictptr != NULL) { + dict = *dictptr; + } + else { + dict = NULL; + } + } + if (dict != NULL) { + Py_INCREF(dict); + PyObject *attr = PyDict_GetItemWithError(dict, name); + if (attr != NULL) { + *method = Py_NewRef(attr); Py_DECREF(dict); + Py_XDECREF(descr); + return 0; + } + Py_DECREF(dict); - if (PyErr_Occurred()) { - Py_XDECREF(descr); - return 0; - } + if (PyErr_Occurred()) { + Py_XDECREF(descr); + return 0; } } @@ -1230,7 +1383,7 @@ _PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method) } PyErr_Format(PyExc_AttributeError, - "'%.50s' object has no attribute '%U'", + "'%.100s' object has no attribute '%U'", tp->tp_name, name); set_attribute_error_context(obj, name); @@ -1253,7 +1406,6 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name, PyObject *descr = NULL; PyObject *res = NULL; descrgetfunc f; - PyObject **dictptr; if (!PyUnicode_Check(name)){ PyErr_Format(PyExc_TypeError, @@ -1263,7 +1415,7 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name, } Py_INCREF(name); - if (tp->tp_dict == NULL) { + if (!_PyType_IsReady(tp)) { if (PyType_Ready(tp) < 0) goto done; } @@ -1284,30 +1436,31 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name, } } if (dict == NULL) { - if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) && - *_PyObject_ValuesPointer(obj)) - { - PyDictValues **values_ptr = _PyObject_ValuesPointer(obj); - if (PyUnicode_CheckExact(name)) { - assert(*_PyObject_DictPointer(obj) == NULL); - res = _PyObject_GetInstanceAttribute(obj, *values_ptr, name); - if (res != NULL) { - goto done; + if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT)) { + PyDictOrValues* dorv_ptr = _PyObject_DictOrValuesPointer(obj); + if (_PyDictOrValues_IsValues(*dorv_ptr)) { + PyDictValues *values = _PyDictOrValues_GetValues(*dorv_ptr); + if (PyUnicode_CheckExact(name)) { + res = _PyObject_GetInstanceAttribute(obj, values, name); + if (res != NULL) { + goto done; + } + } + else { + dict = _PyObject_MakeDictFromInstanceAttributes(obj, values); + if (dict == NULL) { + res = NULL; + goto done; + } + dorv_ptr->dict = dict; } } else { - dictptr = _PyObject_DictPointer(obj); - assert(dictptr != NULL && *dictptr == NULL); - *dictptr = dict = _PyObject_MakeDictFromInstanceAttributes(obj, *values_ptr); - if (dict == NULL) { - res = NULL; - goto done; - } - *values_ptr = NULL; + dict = _PyDictOrValues_GetDict(*dorv_ptr); } } else { - dictptr = _PyObject_DictPointer(obj); + PyObject **dictptr = _PyObject_ComputedDictPointer(obj); if (dictptr) { dict = *dictptr; } @@ -1351,7 +1504,7 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name, if (!suppress) { PyErr_Format(PyExc_AttributeError, - "'%.50s' object has no attribute '%U'", + "'%.100s' object has no attribute '%U'", tp->tp_name, name); set_attribute_error_context(obj, name); @@ -1384,8 +1537,9 @@ _PyObject_GenericSetAttrWithDict(PyObject *obj, PyObject *name, return -1; } - if (tp->tp_dict == NULL && PyType_Ready(tp) < 0) + if (!_PyType_IsReady(tp) && PyType_Ready(tp) < 0) { return -1; + } Py_INCREF(name); Py_INCREF(tp); @@ -1401,27 +1555,34 @@ _PyObject_GenericSetAttrWithDict(PyObject *obj, PyObject *name, } if (dict == NULL) { - if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) && *_PyObject_ValuesPointer(obj)) { - res = _PyObject_StoreInstanceAttribute(obj, *_PyObject_ValuesPointer(obj), name, value); + PyObject **dictptr; + if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT)) { + PyDictOrValues *dorv_ptr = _PyObject_DictOrValuesPointer(obj); + if (_PyDictOrValues_IsValues(*dorv_ptr)) { + res = _PyObject_StoreInstanceAttribute( + obj, _PyDictOrValues_GetValues(*dorv_ptr), name, value); + goto error_check; + } + dictptr = &dorv_ptr->dict; } else { - PyObject **dictptr = _PyObject_DictPointer(obj); - if (dictptr == NULL) { - if (descr == NULL) { - PyErr_Format(PyExc_AttributeError, - "'%.100s' object has no attribute '%U'", - tp->tp_name, name); - } - else { - PyErr_Format(PyExc_AttributeError, - "'%.50s' object attribute '%U' is read-only", - tp->tp_name, name); - } - goto done; + dictptr = _PyObject_ComputedDictPointer(obj); + } + if (dictptr == NULL) { + if (descr == NULL) { + PyErr_Format(PyExc_AttributeError, + "'%.100s' object has no attribute '%U'", + tp->tp_name, name); } else { - res = _PyObjectDict_SetItem(tp, dictptr, name, value); + PyErr_Format(PyExc_AttributeError, + "'%.100s' object attribute '%U' is read-only", + tp->tp_name, name); } + goto done; + } + else { + res = _PyObjectDict_SetItem(tp, dictptr, name, value); } } else { @@ -1432,6 +1593,7 @@ _PyObject_GenericSetAttrWithDict(PyObject *obj, PyObject *name, res = PyDict_SetItem(dict, name, value); Py_DECREF(dict); } + error_check: if (res < 0 && PyErr_ExceptionMatches(PyExc_KeyError)) { if (PyType_IsSubtype(tp, &PyType_Type)) { PyErr_Format(PyExc_AttributeError, @@ -1463,7 +1625,7 @@ PyObject_GenericSetDict(PyObject *obj, PyObject *value, void *context) PyObject **dictptr = _PyObject_GetDictPtr(obj); if (dictptr == NULL) { if (_PyType_HasFeature(Py_TYPE(obj), Py_TPFLAGS_MANAGED_DICT) && - *_PyObject_ValuesPointer(obj) != NULL) + _PyDictOrValues_IsValues(*_PyObject_DictOrValuesPointer(obj))) { /* Was unable to convert to dict */ PyErr_NoMemory(); @@ -1484,8 +1646,7 @@ PyObject_GenericSetDict(PyObject *obj, PyObject *value, void *context) "not a '%.200s'", Py_TYPE(value)->tp_name); return -1; } - Py_INCREF(value); - Py_XSETREF(*dictptr, value); + Py_XSETREF(*dictptr, Py_NewRef(value)); return 0; } @@ -1549,13 +1710,15 @@ _dir_locals(void) PyObject *names; PyObject *locals; - locals = PyEval_GetLocals(); + locals = _PyEval_GetFrameLocals(); if (locals == NULL) return NULL; names = PyMapping_Keys(locals); - if (!names) + Py_DECREF(locals); + if (!names) { return NULL; + } if (!PyList_Check(names)) { PyErr_Format(PyExc_TypeError, "dir(): expected keys() of locals to be a list, " @@ -1567,7 +1730,6 @@ _dir_locals(void) Py_DECREF(names); return NULL; } - /* the locals don't need to be DECREF'd */ return names; } @@ -1624,10 +1786,14 @@ none_repr(PyObject *op) return PyUnicode_FromString("None"); } -static void _Py_NO_RETURN -none_dealloc(PyObject* Py_UNUSED(ignore)) +static void +none_dealloc(PyObject* none) { - _Py_FatalRefcountError("deallocating None"); + /* This should never get called, but we also don't want to SEGV if + * we accidentally decref None out of existence. Instead, + * since None is an immortal object, re-set the reference count. + */ + _Py_SetImmortal(none); } static PyObject * @@ -1646,6 +1812,11 @@ none_bool(PyObject *v) return 0; } +static Py_hash_t none_hash(PyObject *v) +{ + return 0xFCA86420; +} + static PyNumberMethods none_as_number = { 0, /* nb_add */ 0, /* nb_subtract */ @@ -1688,7 +1859,7 @@ PyTypeObject _PyNone_Type = { "NoneType", 0, 0, - none_dealloc, /*tp_dealloc*/ /*never called*/ + none_dealloc, /*tp_dealloc*/ 0, /*tp_vectorcall_offset*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ @@ -1697,7 +1868,7 @@ PyTypeObject _PyNone_Type = { &none_as_number, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ - 0, /*tp_hash */ + (hashfunc)none_hash,/*tp_hash */ 0, /*tp_call */ 0, /*tp_str */ 0, /*tp_getattro */ @@ -1707,7 +1878,7 @@ PyTypeObject _PyNone_Type = { 0, /*tp_doc */ 0, /*tp_traverse */ 0, /*tp_clear */ - 0, /*tp_richcompare */ + _Py_BaseObject_RichCompare, /*tp_richcompare */ 0, /*tp_weaklistoffset */ 0, /*tp_iter */ 0, /*tp_iternext */ @@ -1725,8 +1896,9 @@ PyTypeObject _PyNone_Type = { }; PyObject _Py_NoneStruct = { - _PyObject_EXTRA_INIT - 1, &_PyNone_Type + _PyObject_EXTRA_INIT + { _Py_IMMORTAL_REFCNT }, + &_PyNone_Type }; /* NotImplemented is an object that can be used to signal that an @@ -1759,13 +1931,14 @@ notimplemented_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) Py_RETURN_NOTIMPLEMENTED; } -static void _Py_NO_RETURN -notimplemented_dealloc(PyObject* ignore) +static void +notimplemented_dealloc(PyObject *notimplemented) { /* This should never get called, but we also don't want to SEGV if - * we accidentally decref NotImplemented out of existence. + * we accidentally decref NotImplemented out of existence. Instead, + * since Notimplemented is an immortal object, re-set the reference count. */ - Py_FatalError("deallocating NotImplemented"); + _Py_SetImmortal(notimplemented); } static int @@ -1827,31 +2000,27 @@ PyTypeObject _PyNotImplemented_Type = { PyObject _Py_NotImplementedStruct = { _PyObject_EXTRA_INIT - 1, &_PyNotImplemented_Type + { _Py_IMMORTAL_REFCNT }, + &_PyNotImplemented_Type }; -PyStatus -_PyTypes_InitState(PyInterpreterState *interp) + +void +_PyObject_InitState(PyInterpreterState *interp) { +#ifdef Py_TRACE_REFS if (!_Py_IsMainInterpreter(interp)) { - return _PyStatus_OK(); - } - - PyStatus status = _PyTypes_InitSlotDefs(); - if (_PyStatus_EXCEPTION(status)) { - return status; + init_refchain(interp); } - - return _PyStatus_OK(); +#endif } - -#ifdef MS_WINDOWS -extern PyTypeObject PyHKEY_Type; -#endif extern PyTypeObject _Py_GenericAliasIterType; extern PyTypeObject _PyMemoryIter_Type; +extern PyTypeObject _PyLineIterator; +extern PyTypeObject _PyPositionsIterator; +extern PyTypeObject _PyLegacyEventHandler_Type; static PyTypeObject* static_types[] = { // The two most important base types: must be initialized first and @@ -1897,9 +2066,6 @@ static PyTypeObject* static_types[] = { &PyFunction_Type, &PyGen_Type, &PyGetSetDescr_Type, -#ifdef MS_WINDOWS - &PyHKEY_Type, -#endif &PyInstanceMethod_Type, &PyListIter_Type, &PyListRevIter_Type, @@ -1939,6 +2105,7 @@ static PyTypeObject* static_types[] = { &_PyAsyncGenASend_Type, &_PyAsyncGenAThrow_Type, &_PyAsyncGenWrappedValue_Type, + &_PyBufferWrapper_Type, &_PyContextTokenMissing_Type, &_PyCoroWrapper_Type, &_Py_GenericAliasIterType, @@ -1949,18 +2116,22 @@ static PyTypeObject* static_types[] = { &_PyHamt_BitmapNode_Type, &_PyHamt_CollisionNode_Type, &_PyHamt_Type, + &_PyLegacyEventHandler_Type, &_PyInterpreterID_Type, + &_PyLineIterator, &_PyManagedBuffer_Type, &_PyMemoryIter_Type, &_PyMethodWrapper_Type, &_PyNamespace_Type, &_PyNone_Type, &_PyNotImplemented_Type, + &_PyPositionsIterator, &_PyUnicodeASCIIIter_Type, &_PyUnion_Type, &_PyWeakref_CallableProxyType, &_PyWeakref_ProxyType, &_PyWeakref_RefType, + &_PyTypeAlias_Type, // subclasses: _PyTypes_FiniTypes() deallocates them before their base // class @@ -1976,15 +2147,11 @@ static PyTypeObject* static_types[] = { PyStatus _PyTypes_InitTypes(PyInterpreterState *interp) { - if (!_Py_IsMainInterpreter(interp)) { - return _PyStatus_OK(); - } - // All other static types (unless initialized elsewhere) for (size_t i=0; i < Py_ARRAY_LENGTH(static_types); i++) { PyTypeObject *type = static_types[i]; - if (PyType_Ready(type) < 0) { - return _PyStatus_ERR("Can't initialize types"); + if (_PyStaticType_InitBuiltin(interp, type) < 0) { + return _PyStatus_ERR("Can't initialize builtin type"); } if (type == &PyType_Type) { // Sanitify checks of the two most important types @@ -1993,6 +2160,11 @@ _PyTypes_InitTypes(PyInterpreterState *interp) } } + // Must be after static types are initialized + if (_Py_initialize_generic(interp) < 0) { + return _PyStatus_ERR("Can't initialize generic types"); + } + return _PyStatus_OK(); } @@ -2006,34 +2178,43 @@ _PyTypes_InitTypes(PyInterpreterState *interp) void _PyTypes_FiniTypes(PyInterpreterState *interp) { - if (!_Py_IsMainInterpreter(interp)) { - return; - } - // Deallocate types in the reverse order to deallocate subclasses before // their base classes. for (Py_ssize_t i=Py_ARRAY_LENGTH(static_types)-1; i>=0; i--) { PyTypeObject *type = static_types[i]; - _PyStaticType_Dealloc(type); + _PyStaticType_Dealloc(interp, type); } } -void -_Py_NewReference(PyObject *op) +static inline void +new_reference(PyObject *op) { - if (_Py_tracemalloc_config.tracing) { + if (_PyRuntime.tracemalloc.config.tracing) { _PyTraceMalloc_NewReference(op); } -#ifdef Py_REF_DEBUG - _Py_RefTotal++; -#endif - Py_SET_REFCNT(op, 1); + // Skip the immortal object check in Py_SET_REFCNT; always set refcnt to 1 + op->ob_refcnt = 1; #ifdef Py_TRACE_REFS _Py_AddToAllObjects(op, 1); #endif } +void +_Py_NewReference(PyObject *op) +{ +#ifdef Py_REF_DEBUG + reftotal_increment(_PyInterpreterState_GET()); +#endif + new_reference(op); +} + +void +_Py_NewReferenceNoTotal(PyObject *op) +{ + new_reference(op); +} + #ifdef Py_TRACE_REFS void @@ -2043,7 +2224,8 @@ _Py_ForgetReference(PyObject *op) _PyObject_ASSERT_FAILED_MSG(op, "negative refcnt"); } - if (op == &refchain || + PyObject *refchain = REFCHAIN(_PyInterpreterState_GET()); + if (op == refchain || op->_ob_prev->_ob_next != op || op->_ob_next->_ob_prev != op) { _PyObject_ASSERT_FAILED_MSG(op, "invalid object chain"); @@ -2051,12 +2233,12 @@ _Py_ForgetReference(PyObject *op) #ifdef SLOW_UNREF_CHECK PyObject *p; - for (p = refchain._ob_next; p != &refchain; p = p->_ob_next) { + for (p = refchain->_ob_next; p != refchain; p = p->_ob_next) { if (p == op) { break; } } - if (p == &refchain) { + if (p == refchain) { /* Not found */ _PyObject_ASSERT_FAILED_MSG(op, "object not found in the objects list"); @@ -2072,11 +2254,15 @@ _Py_ForgetReference(PyObject *op) * interpreter must be in a healthy state. */ void -_Py_PrintReferences(FILE *fp) +_Py_PrintReferences(PyInterpreterState *interp, FILE *fp) { PyObject *op; + if (interp == NULL) { + interp = _PyInterpreterState_Main(); + } fprintf(fp, "Remaining objects:\n"); - for (op = refchain._ob_next; op != &refchain; op = op->_ob_next) { + PyObject *refchain = REFCHAIN(interp); + for (op = refchain->_ob_next; op != refchain; op = op->_ob_next) { fprintf(fp, "%p [%zd] ", (void *)op, Py_REFCNT(op)); if (PyObject_Print(op, fp, 0) != 0) { PyErr_Clear(); @@ -2088,34 +2274,42 @@ _Py_PrintReferences(FILE *fp) /* Print the addresses of all live objects. Unlike _Py_PrintReferences, this * doesn't make any calls to the Python C API, so is always safe to call. */ +// XXX This function is not safe to use if the interpreter has been +// freed or is in an unhealthy state (e.g. late in finalization). +// The call in Py_FinalizeEx() is okay since the main interpreter +// is statically allocated. void -_Py_PrintReferenceAddresses(FILE *fp) +_Py_PrintReferenceAddresses(PyInterpreterState *interp, FILE *fp) { PyObject *op; + PyObject *refchain = REFCHAIN(interp); fprintf(fp, "Remaining object addresses:\n"); - for (op = refchain._ob_next; op != &refchain; op = op->_ob_next) + for (op = refchain->_ob_next; op != refchain; op = op->_ob_next) fprintf(fp, "%p [%zd] %s\n", (void *)op, Py_REFCNT(op), Py_TYPE(op)->tp_name); } +/* The implementation of sys.getobjects(). */ PyObject * _Py_GetObjects(PyObject *self, PyObject *args) { int i, n; PyObject *t = NULL; PyObject *res, *op; + PyInterpreterState *interp = _PyInterpreterState_GET(); if (!PyArg_ParseTuple(args, "i|O", &n, &t)) return NULL; - op = refchain._ob_next; + PyObject *refchain = REFCHAIN(interp); + op = refchain->_ob_next; res = PyList_New(0); if (res == NULL) return NULL; - for (i = 0; (n == 0 || i < n) && op != &refchain; i++) { + for (i = 0; (n == 0 || i < n) && op != refchain; i++) { while (op == self || op == args || op == res || op == t || (t != NULL && !Py_IS_TYPE(op, (PyTypeObject *) t))) { op = op->_ob_next; - if (op == &refchain) + if (op == refchain) return res; } if (PyList_Append(res, op) < 0) { @@ -2127,7 +2321,9 @@ _Py_GetObjects(PyObject *self, PyObject *args) return res; } -#endif +#undef REFCHAIN + +#endif /* Py_TRACE_REFS */ /* Hack to force loading of abstract.o */ @@ -2195,9 +2391,8 @@ Py_ReprLeave(PyObject *obj) PyObject *dict; PyObject *list; Py_ssize_t i; - PyObject *error_type, *error_value, *error_traceback; - PyErr_Fetch(&error_type, &error_value, &error_traceback); + PyObject *exc = PyErr_GetRaisedException(); dict = PyThreadState_GetDict(); if (dict == NULL) @@ -2218,7 +2413,7 @@ Py_ReprLeave(PyObject *obj) finally: /* ignore exceptions because there is no way to report them. */ - PyErr_Restore(error_type, error_value, error_traceback); + PyErr_SetRaisedException(exc); } /* Trashcan support. */ @@ -2230,22 +2425,20 @@ finally: * object, with refcount 0. Py_DECREF must already have been called on it. */ static void -_PyTrash_thread_deposit_object(PyObject *op) +_PyTrash_thread_deposit_object(struct _py_trashcan *trash, PyObject *op) { - PyThreadState *tstate = _PyThreadState_GET(); _PyObject_ASSERT(op, _PyObject_IS_GC(op)); _PyObject_ASSERT(op, !_PyObject_GC_IS_TRACKED(op)); _PyObject_ASSERT(op, Py_REFCNT(op) == 0); - _PyGCHead_SET_PREV(_Py_AS_GC(op), tstate->trash_delete_later); - tstate->trash_delete_later = op; + _PyGCHead_SET_PREV(_Py_AS_GC(op), (PyGC_Head*)trash->delete_later); + trash->delete_later = op; } /* Deallocate all the objects in the gcstate->trash_delete_later list. * Called when the call-stack unwinds again. */ static void -_PyTrash_thread_destroy_chain(void) +_PyTrash_thread_destroy_chain(struct _py_trashcan *trash) { - PyThreadState *tstate = _PyThreadState_GET(); /* We need to increase trash_delete_nesting here, otherwise, _PyTrash_thread_destroy_chain will be called recursively and then possibly crash. An example that may crash without @@ -2257,13 +2450,13 @@ _PyTrash_thread_destroy_chain(void) tups = [(tup,) for tup in tups] del tups */ - assert(tstate->trash_delete_nesting == 0); - ++tstate->trash_delete_nesting; - while (tstate->trash_delete_later) { - PyObject *op = tstate->trash_delete_later; + assert(trash->delete_nesting == 0); + ++trash->delete_nesting; + while (trash->delete_later) { + PyObject *op = trash->delete_later; destructor dealloc = Py_TYPE(op)->tp_dealloc; - tstate->trash_delete_later = + trash->delete_later = (PyObject*) _PyGCHead_PREV(_Py_AS_GC(op)); /* Call the deallocator directly. This used to try to @@ -2274,22 +2467,64 @@ _PyTrash_thread_destroy_chain(void) */ _PyObject_ASSERT(op, Py_REFCNT(op) == 0); (*dealloc)(op); - assert(tstate->trash_delete_nesting == 1); + assert(trash->delete_nesting == 1); + } + --trash->delete_nesting; +} + + +static struct _py_trashcan * +_PyTrash_get_state(PyThreadState *tstate) +{ + if (tstate != NULL) { + return &tstate->trash; + } + // The current thread must be finalizing. + // Fall back to using thread-local state. + // XXX Use thread-local variable syntax? + assert(PyThread_tss_is_created(&_PyRuntime.trashTSSkey)); + struct _py_trashcan *trash = + (struct _py_trashcan *)PyThread_tss_get(&_PyRuntime.trashTSSkey); + if (trash == NULL) { + trash = PyMem_RawMalloc(sizeof(struct _py_trashcan)); + if (trash == NULL) { + Py_FatalError("Out of memory"); + } + PyThread_tss_set(&_PyRuntime.trashTSSkey, (void *)trash); + } + return trash; +} + +static void +_PyTrash_clear_state(PyThreadState *tstate) +{ + if (tstate != NULL) { + assert(tstate->trash.delete_later == NULL); + return; + } + if (PyThread_tss_is_created(&_PyRuntime.trashTSSkey)) { + struct _py_trashcan *trash = + (struct _py_trashcan *)PyThread_tss_get(&_PyRuntime.trashTSSkey); + if (trash != NULL) { + PyThread_tss_set(&_PyRuntime.trashTSSkey, (void *)NULL); + PyMem_RawFree(trash); + } } - --tstate->trash_delete_nesting; } int _PyTrash_begin(PyThreadState *tstate, PyObject *op) { - if (tstate->trash_delete_nesting >= _PyTrash_UNWIND_LEVEL) { + // XXX Make sure the GIL is held. + struct _py_trashcan *trash = _PyTrash_get_state(tstate); + if (trash->delete_nesting >= _PyTrash_UNWIND_LEVEL) { /* Store the object (to be deallocated later) and jump past * Py_TRASHCAN_END, skipping the body of the deallocator */ - _PyTrash_thread_deposit_object(op); + _PyTrash_thread_deposit_object(trash, op); return 1; } - ++tstate->trash_delete_nesting; + ++trash->delete_nesting; return 0; } @@ -2297,9 +2532,14 @@ _PyTrash_begin(PyThreadState *tstate, PyObject *op) void _PyTrash_end(PyThreadState *tstate) { - --tstate->trash_delete_nesting; - if (tstate->trash_delete_later && tstate->trash_delete_nesting <= 0) { - _PyTrash_thread_destroy_chain(); + // XXX Make sure the GIL is held. + struct _py_trashcan *trash = _PyTrash_get_state(tstate); + --trash->delete_nesting; + if (trash->delete_nesting <= 0) { + if (trash->delete_later != NULL) { + _PyTrash_thread_destroy_chain(trash); + } + _PyTrash_clear_state(tstate); } } @@ -2347,14 +2587,9 @@ _PyObject_AssertFailed(PyObject *obj, const char *expr, const char *msg, /* Display the traceback where the object has been allocated. Do it before dumping repr(obj), since repr() is more likely to crash than dumping the traceback. */ - void *ptr; PyTypeObject *type = Py_TYPE(obj); - if (_PyType_IS_GC(type)) { - ptr = (void *)((char *)obj - sizeof(PyGC_Head)); - } - else { - ptr = (void *)obj; - } + const size_t presize = _PyType_PreHeaderSize(type); + void *ptr = (void *)((char *)obj - presize); _PyMem_DumpTraceback(fileno(stderr), ptr); /* This might succeed or fail, but we're about to abort, so at least @@ -2376,10 +2611,10 @@ _Py_Dealloc(PyObject *op) destructor dealloc = type->tp_dealloc; #ifdef Py_DEBUG PyThreadState *tstate = _PyThreadState_GET(); - PyObject *old_exc_type = tstate->curexc_type; + PyObject *old_exc = tstate != NULL ? tstate->current_exception : NULL; // Keep the old exception type alive to prevent undefined behavior // on (tstate->curexc_type != old_exc_type) below - Py_XINCREF(old_exc_type); + Py_XINCREF(old_exc); // Make sure that type->tp_name remains valid Py_INCREF(type); #endif @@ -2392,12 +2627,12 @@ _Py_Dealloc(PyObject *op) #ifdef Py_DEBUG // gh-89373: The tp_dealloc function must leave the current exception // unchanged. - if (tstate->curexc_type != old_exc_type) { + if (tstate != NULL && tstate->current_exception != old_exc) { const char *err; - if (old_exc_type == NULL) { + if (old_exc == NULL) { err = "Deallocator of type '%s' raised an exception"; } - else if (tstate->curexc_type == NULL) { + else if (tstate->current_exception == NULL) { err = "Deallocator of type '%s' cleared the current exception"; } else { @@ -2408,7 +2643,7 @@ _Py_Dealloc(PyObject *op) } _Py_FatalErrorFormat(__func__, err, type->tp_name); } - Py_XDECREF(old_exc_type); + Py_XDECREF(old_exc); Py_DECREF(type); #endif } |