diff --git a/Include/cpython/object.h b/Include/cpython/object.h index e624326693d8e7..a247d1bde38aaf 100644 --- a/Include/cpython/object.h +++ b/Include/cpython/object.h @@ -268,6 +268,7 @@ typedef struct _heaptypeobject { PyObject *ht_name, *ht_slots, *ht_qualname; struct _dictkeysobject *ht_cached_keys; PyObject *ht_module; + PyType_Spec *ht_static_spec; char *_ht_tpname; // Storage for "tp_name"; see PyType_FromModuleAndSpec struct _specialization_cache _spec_cache; // For use by the specializer. /* here are optional user slots, followed by the members. */ diff --git a/Include/internal/pycore_typeobject.h b/Include/internal/pycore_typeobject.h index 7e533bd138469b..4e2a6942ad895d 100644 --- a/Include/internal/pycore_typeobject.h +++ b/Include/internal/pycore_typeobject.h @@ -165,6 +165,7 @@ extern PyObject * _PyType_GetMRO(PyTypeObject *type); extern PyObject* _PyType_GetSubclasses(PyTypeObject *); extern int _PyType_HasSubclasses(PyTypeObject *); PyAPI_FUNC(PyObject *) _PyType_GetModuleByDef2(PyTypeObject *, PyTypeObject *, PyModuleDef *); +PyAPI_FUNC(PyTypeObject *) _PyType_GetBaseBySpec(PyTypeObject *, PyType_Spec *); // PyType_Ready() must be called if _PyType_IsReady() is false. // See also the Py_TPFLAGS_READY flag. diff --git a/Include/typeslots.h b/Include/typeslots.h index 506b05580de146..6fdd636f57d1a3 100644 --- a/Include/typeslots.h +++ b/Include/typeslots.h @@ -86,3 +86,7 @@ /* New in 3.10 */ #define Py_am_send 81 #endif +#if !defined(Py_LIMITED_API) +/* New in 3.13 */ +#define Py_id_static_spec 82 +#endif diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index ee3bd0092f9bf3..085b82a5993f1f 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1674,7 +1674,7 @@ def delx(self): del self.__x '3P' # PyMappingMethods '10P' # PySequenceMethods '2P' # PyBufferProcs - '6P' + '7P' '1PIP' # Specializer cache ) class newstyleclass(object): pass diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 574cb8014493c4..a3183ed58f07af 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -451,23 +451,44 @@ class _ctypes.CType_Type "PyObject *" "clinic_state()->CType_Type" [clinic start generated code]*/ /*[clinic end generated code: output=da39a3ee5e6b4b0d input=8389fc5b74a84f2a]*/ + +static PyType_Spec pyctype_type_spec; + + +static inline int +PyStgInfo_FromSpec(PyObject *type, StgInfo **result) +{ + *result = NULL; + PyTypeObject *PyCType_Type = _PyType_GetBaseBySpec(Py_TYPE(type), + &pyctype_type_spec); + if (PyCType_Type == NULL) { + // not a ctypes class. + return -1; + } + StgInfo *info = PyObject_GetTypeData(type, PyCType_Type); + assert(info != NULL); + if (!info->initialized) { + // StgInfo is not initialized. This happens in abstract classes. + return 0; + } + *result = info; + return 1; +} + static int CType_Type_traverse(PyObject *self, visitproc visit, void *arg) { - ctypes_state *st = get_module_state_by_def_final(Py_TYPE(self)); - if (st && st->PyCType_Type) { - StgInfo *info; - if (PyStgInfo_FromType(st, self, &info) < 0) { - PyErr_WriteUnraisable(self); - } - if (info) { - Py_VISIT(info->proto); - Py_VISIT(info->argtypes); - Py_VISIT(info->converters); - Py_VISIT(info->restype); - Py_VISIT(info->checker); - Py_VISIT(info->module); - } + StgInfo *info; + if (PyStgInfo_FromSpec(self, &info) < 0) { + PyErr_WriteUnraisable(self); + } + if (info) { + Py_VISIT(info->proto); + Py_VISIT(info->argtypes); + Py_VISIT(info->converters); + Py_VISIT(info->restype); + Py_VISIT(info->checker); + Py_VISIT(info->module); } Py_VISIT(Py_TYPE(self)); return PyType_Type.tp_traverse(self, visit, arg); @@ -488,15 +509,12 @@ ctype_clear_stginfo(StgInfo *info) static int CType_Type_clear(PyObject *self) { - ctypes_state *st = get_module_state_by_def_final(Py_TYPE(self)); - if (st && st->PyCType_Type) { - StgInfo *info; - if (PyStgInfo_FromType(st, self, &info) < 0) { - PyErr_WriteUnraisable(self); - } - if (info) { - ctype_clear_stginfo(info); - } + StgInfo *info; + if (PyStgInfo_FromSpec(self, &info) < 0) { + PyErr_WriteUnraisable(self); + } + if (info) { + ctype_clear_stginfo(info); } return PyType_Type.tp_clear(self); } @@ -504,21 +522,18 @@ CType_Type_clear(PyObject *self) static void CType_Type_dealloc(PyObject *self) { - ctypes_state *st = get_module_state_by_def_final(Py_TYPE(self)); - if (st && st->PyCType_Type) { - StgInfo *info; - if (PyStgInfo_FromType(st, self, &info) < 0) { - PyErr_WriteUnraisable(self); - } - if (info) { - PyMem_Free(info->ffi_type_pointer.elements); - info->ffi_type_pointer.elements = NULL; - PyMem_Free(info->format); - info->format = NULL; - PyMem_Free(info->shape); - info->shape = NULL; - ctype_clear_stginfo(info); - } + StgInfo *info; + if (PyStgInfo_FromSpec(self, &info) < 0) { + PyErr_WriteUnraisable(self); + } + if (info) { + PyMem_Free(info->ffi_type_pointer.elements); + info->ffi_type_pointer.elements = NULL; + PyMem_Free(info->format); + info->format = NULL; + PyMem_Free(info->shape); + info->shape = NULL; + ctype_clear_stginfo(info); } PyTypeObject *tp = Py_TYPE(self); PyType_Type.tp_dealloc(self); @@ -574,6 +589,8 @@ static PyType_Slot ctype_type_slots[] = { {Py_tp_methods, ctype_methods}, // Sequence protocol. {Py_sq_repeat, CType_Type_repeat}, + // Memory layout ID. + {Py_id_static_spec, &pyctype_type_spec}, {0, NULL}, }; diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index 20c68134be2804..feb51b8602d762 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -101,20 +101,6 @@ get_module_state_by_def(PyTypeObject *cls) return get_module_state(mod); } -static inline ctypes_state * -get_module_state_by_def_final(PyTypeObject *cls) -{ - if (cls->tp_mro == NULL) { - return NULL; - } - PyObject *mod = PyType_GetModuleByDef(cls, &_ctypesmodule); - if (mod == NULL) { - PyErr_Clear(); - return NULL; - } - return get_module_state(mod); -} - extern PyType_Spec carg_spec; extern PyType_Spec cfield_spec; diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 4b144fab5de8f1..8fb13f87be9fde 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -3635,6 +3635,7 @@ type_new_alloc(type_new_ctx *ctx) et->ht_name = Py_NewRef(ctx->name); et->ht_module = NULL; et->_ht_tpname = NULL; + et->ht_static_spec = NULL; _PyObject_SetDeferredRefcount((PyObject *)et); @@ -4661,6 +4662,17 @@ _PyType_FromMetaclass_impl( } } break; + case Py_id_static_spec: + { + if (slot->pfunc && slot->pfunc != spec) { + PyErr_Format(PyExc_TypeError, + "Py_id_static_spec slot does not match " + "the type spec of '%s'", type->tp_name); + goto finally; + } + res->ht_static_spec = (PyType_Spec *)slot->pfunc; + } + break; default: { /* Copy other slots directly */ @@ -4823,6 +4835,9 @@ PyType_GetSlot(PyTypeObject *type, int slot) PyErr_BadInternalCall(); return NULL; } + if (slot == Py_id_static_spec) { + return NULL; + } parent_slot = *(void**)((char*)type + pyslot_offsets[slot].slot_offset); if (parent_slot == NULL) { @@ -4953,6 +4968,63 @@ _PyType_GetModuleByDef2(PyTypeObject *left, PyTypeObject *right, return module; } +static PyTypeObject * +get_base_by_spec_recursive(PyTypeObject *type, PyType_Spec *spec) +{ + PyObject *bases = type->tp_bases; + Py_ssize_t n = PyTuple_GET_SIZE(bases); + for (Py_ssize_t i = 0; i < n; i++) { + PyTypeObject *base = (PyTypeObject *)PyTuple_GET_ITEM(bases, i); + if (!_PyType_HasFeature(base, Py_TPFLAGS_HEAPTYPE)) { + continue; + } + if (((PyHeapTypeObject*)base)->ht_static_spec == spec) { + return base; + } + base = get_base_by_spec_recursive(base, spec); + if (base) { + return base; + } + } + return NULL; +} + +PyTypeObject * +_PyType_GetBaseBySpec(PyTypeObject *type, PyType_Spec *spec) +{ + assert(spec); + assert(PyType_Check(type)); + PyTypeObject *res = NULL; + + BEGIN_TYPE_LOCK() + PyObject *mro = lookup_tp_mro(type); + if (mro == NULL) { + res = get_base_by_spec_recursive(type, spec); + } + else { + assert(PyTuple_Check(mro)); + for (Py_ssize_t i = PyTuple_GET_SIZE(mro) - 1; i >= 0; i--) { + PyObject *super = PyTuple_GET_ITEM(mro, i); + if(!_PyType_HasFeature((PyTypeObject *)super, Py_TPFLAGS_HEAPTYPE)) { + continue; + } + if (((PyHeapTypeObject*)super)->ht_static_spec == spec) { + res = _PyType_CAST(super); + break; + } + } + } + END_TYPE_LOCK() + + if (res == NULL) { + PyErr_Format( + PyExc_TypeError, + "PyType_GetBaseBySpec: No superclass of '%s' has the given spec", + type->tp_name); + } + return res; +} + void * PyObject_GetTypeData(PyObject *obj, PyTypeObject *cls) { @@ -5633,6 +5705,7 @@ type_dealloc(PyObject *self) } Py_XDECREF(et->ht_module); PyMem_Free(et->_ht_tpname); + et->ht_static_spec = NULL; Py_TYPE(type)->tp_free((PyObject *)type); } diff --git a/Objects/typeslots.inc b/Objects/typeslots.inc index 896daa7d8066b7..303a95d844e34d 100644 --- a/Objects/typeslots.inc +++ b/Objects/typeslots.inc @@ -80,3 +80,4 @@ {offsetof(PyAsyncMethods, am_anext), offsetof(PyTypeObject, tp_as_async)}, {-1, offsetof(PyTypeObject, tp_finalize)}, {offsetof(PyAsyncMethods, am_send), offsetof(PyTypeObject, tp_as_async)}, +{-1, offsetof(PyHeapTypeObject, ht_static_spec)}, diff --git a/Objects/typeslots.py b/Objects/typeslots.py index 8ab05f91be12b0..fbad29b4a4707e 100755 --- a/Objects/typeslots.py +++ b/Objects/typeslots.py @@ -30,6 +30,8 @@ def generate_typeslots(out=sys.stdout): elif member.startswith("bf_"): member = (f'{{offsetof(PyBufferProcs, {member}),'+ ' offsetof(PyTypeObject, tp_as_buffer)}') + elif member == "id_static_spec": + member = '{-1, offsetof(PyHeapTypeObject, ht_static_spec)}' res[int(m.group(2))] = member M = max(res.keys())+1