diff options
Diffstat (limited to 'sources/shiboken6/libshiboken/pep384impl.cpp')
-rw-r--r-- | sources/shiboken6/libshiboken/pep384impl.cpp | 571 |
1 files changed, 432 insertions, 139 deletions
diff --git a/sources/shiboken6/libshiboken/pep384impl.cpp b/sources/shiboken6/libshiboken/pep384impl.cpp index 7678fe661..f926107e2 100644 --- a/sources/shiboken6/libshiboken/pep384impl.cpp +++ b/sources/shiboken6/libshiboken/pep384impl.cpp @@ -1,41 +1,7 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#define PEP384_INTERN #include "sbkpython.h" #include "autodecref.h" @@ -44,8 +10,7 @@ #include "basewrapper.h" #include "basewrapper_p.h" #include "sbkenum.h" -#include "sbkenum_p.h" -#include "sbkconverter.h" +#include "voidptr.h" #include <cstdlib> #include <cstring> @@ -54,7 +19,7 @@ extern "C" { /* - * The documentation is located in pep384impl_doc.rst + * The documentation is located in `sources/pyside6/doc/developer/limited_api.rst`. * Here is the verification code for PyTypeObject. * We create a type object and check if its fields @@ -72,16 +37,16 @@ dummy_func(PyObject * /* self */, PyObject * /* args */) } static struct PyMethodDef probe_methoddef[] = { - {"dummy", dummy_func, METH_NOARGS}, - {nullptr} + {"dummy", dummy_func, METH_NOARGS, nullptr}, + {nullptr, nullptr, 0, nullptr} }; static PyGetSetDef probe_getseters[] = { - {nullptr} /* Sentinel */ + {nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */ }; static PyMemberDef probe_members[] = { - {nullptr} /* Sentinel */ + {nullptr, 0, 0, 0, nullptr} /* Sentinel */ }; #define probe_tp_dealloc make_dummy(1) @@ -152,6 +117,8 @@ check_PyTypeObject_valid() PyObject *d = PyObject_GetAttr(obtype, Shiboken::PyMagicName::dictoffset()); long probe_tp_dictoffset = PyLong_AsLong(d); PyObject *probe_tp_mro = PyObject_GetAttr(obtype, Shiboken::PyMagicName::mro()); + Shiboken::AutoDecRef tpDict(PepType_GetDict(check)); + auto *checkDict = tpDict.object(); if (false || strcmp(probe_tp_name, check->tp_name) != 0 || probe_tp_basicsize != check->tp_basicsize @@ -168,8 +135,8 @@ check_PyTypeObject_valid() || probe_tp_methods != check->tp_methods || probe_tp_getset != check->tp_getset || probe_tp_base != typetype->tp_base - || !PyDict_Check(check->tp_dict) - || !PyDict_GetItemString(check->tp_dict, "dummy") + || !PyDict_Check(checkDict) + || !PyDict_GetItemString(checkDict, "dummy") || probe_tp_descr_get != check->tp_descr_get || probe_tp_descr_set != check->tp_descr_set || probe_tp_dictoffset != typetype->tp_dictoffset @@ -190,10 +157,6 @@ check_PyTypeObject_valid() Py_DECREF(probe_tp_mro); } -#if PY_VERSION_HEX < PY_ISSUE33738_SOLVED -#include "pep384_issue33738.cpp" -#endif - #endif // Py_LIMITED_API /***************************************************************************** @@ -215,7 +178,7 @@ static PyObject * find_name_in_mro(PyTypeObject *type, PyObject *name, int *error) { Py_ssize_t i, n; - PyObject *mro, *res, *base, *dict; + PyObject *mro, *res, *base; /* Look in tp_dict of types in MRO */ mro = type->tp_mro; @@ -229,9 +192,10 @@ find_name_in_mro(PyTypeObject *type, PyObject *name, int *error) for (i = 0; i < n; i++) { base = PyTuple_GET_ITEM(mro, i); assert(PyType_Check(base)); - dict = ((PyTypeObject *)base)->tp_dict; - assert(dict && PyDict_Check(dict)); - res = PyDict_GetItem(dict, name); + auto *type = reinterpret_cast<PyTypeObject *>(base); + Shiboken::AutoDecRef dict(PepType_GetDict(type)); + assert(!dict.isNull() && PyDict_Check(dict.object())); + res = PyDict_GetItem(dict.object(), name); if (res != nullptr) break; if (PyErr_Occurred()) { @@ -284,20 +248,173 @@ _PepType_Lookup(PyTypeObject *type, PyObject *name) */ #ifdef Py_LIMITED_API -char * -_PepUnicode_AsString(PyObject *str) +// structs and macros modelled after their equivalents in +// cpython/Include/cpython/unicodeobject.h + +struct PepASCIIObject // since 3.12 +{ + PyObject_HEAD + Py_ssize_t length; /* Number of code points in the string */ + Py_hash_t hash; /* Hash value; -1 if not set */ + struct { + unsigned int interned:2; + unsigned int kind:3; + unsigned int compact:1; + unsigned int ascii:1; + unsigned int ready:1; + unsigned int :24; + } state; +}; + +struct PepASCIIObject_311 : public PepASCIIObject +{ + wchar_t *wstr; /* wchar_t representation (null-terminated) */ +}; + +struct PepCompactUnicodeObject // since 3.12 +{ + PepASCIIObject _base; + Py_ssize_t utf8_length; + char *utf8; /* UTF-8 representation (null-terminated) */ +}; + +struct PepCompactUnicodeObject_311 // since 3.12 +{ + PepASCIIObject_311 _base; + Py_ssize_t utf8_length; + char *utf8; /* UTF-8 representation (null-terminated) */ + Py_ssize_t wstr_length; /* Number of code points in wstr */ +}; + +struct PepUnicodeObject // since 3.12 +{ + PepCompactUnicodeObject _base; + union { + void *any; + Py_UCS1 *latin1; + Py_UCS2 *ucs2; + Py_UCS4 *ucs4; + } data; /* Canonical, smallest-form Unicode buffer */ +}; + +struct PepUnicodeObject_311 +{ + PepCompactUnicodeObject_311 _base; + union { + void *any; + Py_UCS1 *latin1; + Py_UCS2 *ucs2; + Py_UCS4 *ucs4; + } data; /* Canonical, smallest-form Unicode buffer */ +}; + +int _PepUnicode_KIND(PyObject *str) +{ + return reinterpret_cast<PepASCIIObject *>(str)->state.kind; +} + +int _PepUnicode_IS_ASCII(PyObject *str) +{ + auto *asciiObj = reinterpret_cast<PepASCIIObject *>(str); + return asciiObj->state.ascii; +} + +int _PepUnicode_IS_COMPACT(PyObject *str) +{ + auto *asciiObj = reinterpret_cast<PepASCIIObject *>(str); + return asciiObj->state.compact; +} + +static void *_PepUnicode_ASCII_DATA(PyObject *str) +{ + if (_PepRuntimeVersion() < 0x030C00) { + auto *asciiObj_311 = reinterpret_cast<PepASCIIObject_311 *>(str); + return asciiObj_311 + 1; + } + auto *asciiObj = reinterpret_cast<PepASCIIObject *>(str); + return asciiObj + 1; +} + +static void *_PepUnicode_COMPACT_DATA(PyObject *str) +{ + if (_PepUnicode_IS_ASCII(str) != 0) + return _PepUnicode_ASCII_DATA(str); + if (_PepRuntimeVersion() < 0x030C00) { + auto *compactObj_311 = reinterpret_cast<PepCompactUnicodeObject_311 *>(str); + return compactObj_311 + 1; + } + auto *compactObj = reinterpret_cast<PepCompactUnicodeObject *>(str); + return compactObj + 1; +} + +static void *_PepUnicode_NONCOMPACT_DATA(PyObject *str) +{ + return _PepRuntimeVersion() < 0x030C00 + ? reinterpret_cast<PepUnicodeObject_311 *>(str)->data.any + : reinterpret_cast<PepUnicodeObject *>(str)->data.any; +} + +void *_PepUnicode_DATA(PyObject *str) +{ + return _PepUnicode_IS_COMPACT(str) + ? _PepUnicode_COMPACT_DATA(str) : _PepUnicode_NONCOMPACT_DATA(str); +} + +// Fast path accessing UTF8 data without doing a conversion similar +// to _PyUnicode_AsUTF8String +static const char *utf8FastPath_311(PyObject *str) +{ + if (PyUnicode_GetLength(str) == 0) + return ""; + auto *asciiObj = reinterpret_cast<PepASCIIObject_311 *>(str); + if (asciiObj->state.kind != PepUnicode_1BYTE_KIND || asciiObj->state.compact == 0) + return nullptr; // Empirical: PyCompactUnicodeObject.utf8 is only valid for 1 byte + if (asciiObj->state.ascii) { + auto *data = asciiObj + 1; + return reinterpret_cast<const char *>(data); + } + auto *compactObj = reinterpret_cast<PepCompactUnicodeObject_311 *>(str); + if (compactObj->utf8_length) + return compactObj->utf8; + return nullptr; +} + +static const char *utf8FastPath(PyObject *str) +{ + if (PyUnicode_GetLength(str) == 0) + return ""; + auto *asciiObj = reinterpret_cast<PepASCIIObject *>(str); + if (asciiObj->state.kind != PepUnicode_1BYTE_KIND || asciiObj->state.compact == 0) + return nullptr; // Empirical: PyCompactUnicodeObject.utf8 is only valid for 1 byte + if (asciiObj->state.ascii) { + auto *data = asciiObj + 1; + return reinterpret_cast<const char *>(data); + } + auto *compactObj = reinterpret_cast<PepCompactUnicodeObject *>(str); + if (compactObj->utf8_length) + return compactObj->utf8; + return nullptr; +} + +const char *_PepUnicode_AsString(PyObject *str) { /* - * We need to keep the string alive but cannot borrow the Python object. - * Ugly easy way out: We re-code as an interned bytes string. This - * produces a pseudo-leak as long as there are new strings. - * Typically, this function is used for name strings, and the dict size - * will not grow so much. + * This function is the surrogate for PyUnicode_AsUTF8, which keeps the data + * in the unicode object as long as that object exists. + * + * The function does too much if not optimized by utf8, because it keeps the + * string alive, unconditionally. + * We should not rely on this behavior and think of PyUnicode_AsUTF8, only. */ #define STRINGIFY(x) #x #define TOSTRING(x) STRINGIFY(x) #define AT __FILE__ ":" TOSTRING(__LINE__) + if (const auto *utf8 = _PepRuntimeVersion() < 0x030C00 + ? utf8FastPath_311(str) : utf8FastPath(str)) { + return utf8; + } + static PyObject *cstring_dict = nullptr; if (cstring_dict == nullptr) { cstring_dict = PyDict_New(); @@ -320,38 +437,6 @@ _PepUnicode_AsString(PyObject *str) /***************************************************************************** * - * Support for longobject.h - * - */ -#ifdef Py_LIMITED_API - -/* - * This is the original Python function _PyLong_AsInt() from longobject.c . - * We define it here because we are not allowed to use the function - * from Python with an underscore. - */ - -/* Get a C int from an int object or any object that has an __int__ - method. Return -1 and set an error if overflow occurs. */ - -int -_PepLong_AsInt(PyObject *obj) -{ - int overflow; - long result = PyLong_AsLongAndOverflow(obj, &overflow); - if (overflow || result > INT_MAX || result < INT_MIN) { - /* XXX: could be cute and give a different - message for overflow == -1 */ - PyErr_SetString(PyExc_OverflowError, - "Python int too large to convert to C int"); - return -1; - } - return int(result); -} -#endif // Py_LIMITED_API - -/***************************************************************************** - * * Support for pydebug.h * */ @@ -419,8 +504,24 @@ PepCode_Get(PepCodeObject *co, const char *name) } return ret; } + +int PepCode_Check(PyObject *o) +{ + return o != nullptr && std::strcmp(Py_TYPE(o)->tp_name, "code") == 0 ? 1 : 0; +} + #endif // Py_LIMITED_API +#if defined(Py_LIMITED_API) || defined(PYPY_VERSION) +PyObject *PepFunction_GetDefaults(PyObject *function) +{ + auto *ob_ret = PyObject_GetAttrString(function, "__defaults__"); + Py_XDECREF(ob_ret); // returns borrowed ref + return ob_ret != Py_None ? ob_ret : nullptr; +} + +#endif // defined(Py_LIMITED_API) || defined(PYPY_VERSION) + /***************************************************************************** * * Support for datetime.h @@ -648,6 +749,29 @@ PyStaticMethod_New(PyObject *callable) } #endif // Py_LIMITED_API +#ifdef PYPY_VERSION +PyTypeObject *PepBuiltinMethod_TypePtr = nullptr; + +static PyTypeObject * +getBuiltinMethodType(void) +{ + // PYSIDE-535: PyPy has a special builtin method that acts almost like PyCFunction. + // + // There is no public declaration for the "builtin method" type. + // We also cannot grep it with a Python script since the import is too early. + // Pick a demo "builtin method" by using the VoidPtr type. + // Create the equivalent of + // "from shiboken6.Shiboken import VoidPtr\n" + // "result = type(VoidPtr(0).toBytes)\n"; + auto *pyVoidP = reinterpret_cast<PyObject *>(SbkVoidPtr_TypeF()); + Shiboken::AutoDecRef arg(Py_BuildValue("i", 0)); + Shiboken::AutoDecRef inst(PyObject_CallFunctionObjArgs(pyVoidP, arg.object(), nullptr)); + Shiboken::AutoDecRef meth(PyObject_GetAttrString(inst, "toBytes")); + auto *result = reinterpret_cast<PyTypeObject *>(PyObject_Type(meth)); + return result; +} +#endif + /***************************************************************************** * * Common newly needed functions @@ -667,12 +791,37 @@ PepType_GetNameStr(PyTypeObject *type) return ret; } +// PYSIDE-2264: Find the _functools or functools module and retrieve the +// partial function. This can be tampered with, check carefully. +PyObject * +Pep_GetPartialFunction(void) +{ + static bool initialized = false; + static PyObject *result{}; + if (initialized) { + Py_INCREF(result); + return result; + } + auto *functools = PyImport_ImportModule("_functools"); + if (!functools) { + PyErr_Clear(); + functools = PyImport_ImportModule("functools"); + } + if (!functools) + Py_FatalError("functools cannot be found"); + result = PyObject_GetAttrString(functools, "partial"); + if (!result || !PyCallable_Check(result)) + Py_FatalError("partial not found or not a function"); + initialized = true; + return result; +} + /***************************************************************************** * * Newly introduced convenience functions * */ -#if PY_VERSION_HEX < 0x03070000 || defined(Py_LIMITED_API) +#ifdef Py_LIMITED_API PyObject * PyImport_GetModule(PyObject *name) @@ -698,7 +847,7 @@ PyImport_GetModule(PyObject *name) return m; } -#endif // PY_VERSION_HEX < 0x03070000 || defined(Py_LIMITED_API) +#endif // Py_LIMITED_API // 2020-06-16: For simplicity of creating arbitrary things, this function // is now made public. @@ -723,6 +872,12 @@ PepRun_GetResult(const char *command) return res; } +PyTypeObject *PepType_Type_tp_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) +{ + auto ret = PyType_Type.tp_new(metatype, args, kwds); + return reinterpret_cast<PyTypeObject *>(ret); +} + /***************************************************************************** * * Extra support for name mangling @@ -758,14 +913,14 @@ _Pep_PrivateMangle(PyObject *self, PyObject *name) } Shiboken::AutoDecRef privateobj(PyObject_GetAttr( reinterpret_cast<PyObject *>(Py_TYPE(self)), Shiboken::PyMagicName::name())); -#ifndef Py_LIMITED_API - return _Py_Mangle(privateobj, name); -#else - // For some reason, _Py_Mangle is not in the Limited API. Why? - size_t plen = PyUnicode_GET_LENGTH(privateobj); + + // PYSIDE-1436: _Py_Mangle is no longer exposed; implement it always. + // The rest of this function is our own implementation of _Py_Mangle. + // Please compare the original function in compile.c . + size_t plen = PyUnicode_GET_LENGTH(privateobj.object()); /* Strip leading underscores from class name */ size_t ipriv = 0; - while (PyUnicode_READ_CHAR(privateobj, ipriv) == '_') + while (PyUnicode_READ_CHAR(privateobj.object(), ipriv) == '_') ipriv++; if (ipriv == plen) { Py_INCREF(name); @@ -783,18 +938,17 @@ _Pep_PrivateMangle(PyObject *self, PyObject *name) wchar_t bigbuf[big_stack]; wchar_t *resbuf = amount <= big_stack ? bigbuf : (wchar_t *)malloc(sizeof(wchar_t) * amount); if (!resbuf) - return 0; + return nullptr; /* ident = "_" + priv[ipriv:] + ident # i.e. 1+plen+nlen bytes */ resbuf[0] = '_'; if (PyUnicode_AsWideChar(privateobj, resbuf + 1, ipriv + plen) < 0) - return 0; + return nullptr; if (PyUnicode_AsWideChar(name, resbuf + ipriv + plen + 1, nlen) < 0) - return 0; + return nullptr; PyObject *result = PyUnicode_FromWideChar(resbuf + ipriv, 1 + plen + nlen); if (amount > big_stack) free(resbuf); return result; -#endif // else Py_LIMITED_API } /***************************************************************************** @@ -816,6 +970,21 @@ init_PepRuntime() PepRuntime_38_flag = 1; } +static long _GetPepRuntimeVersion() +{ + auto *version = PySys_GetObject("version_info"); + const auto major = PyLong_AsLong(PyTuple_GetItem(version, 0)); + const auto minor = PyLong_AsLong(PyTuple_GetItem(version, 1)); + const auto micro = PyLong_AsLong(PyTuple_GetItem(version, 2)); + return major << 16 | minor << 8 | micro; +} + +long _PepRuntimeVersion() +{ + static const auto number = _GetPepRuntimeVersion(); + return number; +} + /***************************************************************************** * * PYSIDE-535: Support for PyPy @@ -825,33 +994,108 @@ init_PepRuntime() * */ +/////////////////////////////////////////////////////////////////////// +// +// PEP 697: Support for embedded type structures. +// +// According to `https://docs.python.org/3/c-api/object.html?highlight=pyobject_gettypedata#c.PyObject_GetTypeData` +// the function `PyObject_GetTypeData` should belong to the Stable API +// since version 3.12.0, but it does not. We use instead some copies +// from Python source code. + +#if !defined(Py_LIMITED_API) && PY_VERSION_HEX >= 0x030C0000 + +# define PepObject_GetTypeData PyObject_GetTypeData + +SbkObjectTypePrivate *PepType_SOTP(PyTypeObject *type) +{ + // PYSIDE-2676: Use the meta type explicitly. + // A derived type would fail the offset calculation. + static auto *meta = SbkObjectType_TypeF(); + assert(SbkObjectType_Check(type)); + auto *obType = reinterpret_cast<PyObject *>(type); + void *data = PyObject_GetTypeData(obType, meta); + return reinterpret_cast<SbkObjectTypePrivate *>(data); +} + +void PepType_SOTP_delete(PyTypeObject * /*type*/) +{ +} + +#else + +// The following comments are directly copied from Python 3.12 +// + +// Make sure we have maximum alignment, even if the current compiler +// does not support max_align_t. Note that: +// - Autoconf reports alignment of unknown types to 0. +// - 'long double' has maximum alignment on *most* platforms, +// looks like the best we can do for pre-C11 compilers. +// - The value is tested, see test_alignof_max_align_t +# if !defined(ALIGNOF_MAX_ALIGN_T) || ALIGNOF_MAX_ALIGN_T == 0 +# undef ALIGNOF_MAX_ALIGN_T +# define ALIGNOF_MAX_ALIGN_T alignof(long double) +# endif + +/* Align up to the nearest multiple of alignof(max_align_t) + * (like _Py_ALIGN_UP, but for a size rather than pointer) + */ +static Py_ssize_t _align_up(Py_ssize_t size) +{ + return (size + ALIGNOF_MAX_ALIGN_T - 1) & ~(ALIGNOF_MAX_ALIGN_T - 1); +} + +static void *PepObject_GetTypeData(PyObject *obj, PyTypeObject *cls) +{ + assert(PyObject_TypeCheck(obj, cls)); + return reinterpret_cast<char *>(obj) + _align_up(cls->tp_base->tp_basicsize); +} +// +/////////////////////////////////////////////////////////////////////// + /* - * SbkObjectType extender + * PyTypeObject extender */ -static std::unordered_map<SbkObjectType *, SbkObjectTypePrivate > SOTP_extender{}; -static thread_local SbkObjectType *SOTP_key{}; + +static std::unordered_map<PyTypeObject *, SbkObjectTypePrivate > SOTP_extender{}; +static thread_local PyTypeObject *SOTP_key{}; static thread_local SbkObjectTypePrivate *SOTP_value{}; -SbkObjectTypePrivate *PepType_SOTP(SbkObjectType *sbkType) +SbkObjectTypePrivate *PepType_SOTP(PyTypeObject *type) { - if (sbkType == SOTP_key) + static auto *meta = SbkObjectType_TypeF(); + static bool use_312 = _PepRuntimeVersion() >= 0x030C00; + assert(SbkObjectType_Check(type)); + if (use_312) { + auto *obType = reinterpret_cast<PyObject *>(type); + void *data = PepObject_GetTypeData(obType, meta); + return reinterpret_cast<SbkObjectTypePrivate *>(data); + } + if (type == SOTP_key) return SOTP_value; - auto it = SOTP_extender.find(sbkType); + auto it = SOTP_extender.find(type); if (it == SOTP_extender.end()) { - it = SOTP_extender.insert({sbkType, {}}).first; + it = SOTP_extender.insert({type, {}}).first; memset(&it->second, 0, sizeof(SbkObjectTypePrivate)); } - SOTP_key = sbkType; + SOTP_key = type; SOTP_value = &it->second; return SOTP_value; } -void PepType_SOTP_delete(SbkObjectType *sbkType) +void PepType_SOTP_delete(PyTypeObject *type) { - SOTP_extender.erase(sbkType); + static bool use_312 = _PepRuntimeVersion() >= 0x030C00; + assert(SbkObjectType_Check(type)); + if (use_312) + return; + SOTP_extender.erase(type); SOTP_key = nullptr; } +#endif // !defined(Py_LIMITED_API) && PY_VERSION_HEX >= 0x030C0000 + /* * SbkEnumType extender */ @@ -861,6 +1105,7 @@ static thread_local SbkEnumTypePrivate *SETP_value{}; SbkEnumTypePrivate *PepType_SETP(SbkEnumType *enumType) { + // PYSIDE-2230: This makes no sense at all for Enum types. if (enumType == SETP_key) return SETP_value; auto it = SETP_extender.find(enumType); @@ -879,31 +1124,76 @@ void PepType_SETP_delete(SbkEnumType *enumType) SETP_key = nullptr; } -/* - * PySideQFlagsType extender - */ -static std::unordered_map<PySideQFlagsType *, PySideQFlagsTypePrivate> PFTP_extender{}; -static thread_local PySideQFlagsType *PFTP_key{}; -static thread_local PySideQFlagsTypePrivate *PFTP_value{}; - -PySideQFlagsTypePrivate *PepType_PFTP(PySideQFlagsType *flagsType) +#ifdef Py_LIMITED_API +static PyObject *emulatePyType_GetDict(PyTypeObject *type) { - if (flagsType == PFTP_key) - return PFTP_value; - auto it = PFTP_extender.find(flagsType); - if (it == PFTP_extender.end()) { - it = PFTP_extender.insert({flagsType, {}}).first; - memset(&it->second, 0, sizeof(PySideQFlagsTypePrivate)); + if (_PepRuntimeVersion() < 0x030C00 || type->tp_dict) { + auto *res = type->tp_dict; + Py_XINCREF(res); + return res; } - PFTP_key = flagsType; - PFTP_value = &it->second; - return PFTP_value; + // PYSIDE-2230: Here we are really cheating. We don't know how to + // access an internal dict, and so we simply pretend + // it were an empty dict. This works great for our types. + // This was an unexpectedly simple solution :D + return PyDict_New(); +} +#endif + +// PyType_GetDict: replacement for <static type>.tp_dict, which is +// zero for builtin types since 3.12. +PyObject *PepType_GetDict(PyTypeObject *type) +{ +#if !defined(Py_LIMITED_API) +# if PY_VERSION_HEX >= 0x030C0000 + return PyType_GetDict(type); +# else + // pre 3.12 fallback code, mimicking the addref-behavior. + Py_XINCREF(type->tp_dict); + return type->tp_dict; +# endif +#else + return emulatePyType_GetDict(type); +#endif // Py_LIMITED_API } -void PepType_PFTP_delete(PySideQFlagsType *flagsType) +int PepType_SetDict(PyTypeObject *type, PyObject *dict) { - PFTP_extender.erase(flagsType); - PFTP_key = nullptr; + type->tp_dict = dict; + return 0; +} + +// Pre 3.10, PyType_GetSlot() would only work for heap types. +// FIXME: PyType_GetSlot() can be used unconditionally when the +// minimum limited API version is >= 3.10. +void *PepType_GetSlot(PyTypeObject *type, int aSlot) +{ + static const bool is310 = _PepRuntimeVersion() >= 0x030A00; + if (is310 || (type->tp_flags & Py_TPFLAGS_HEAPTYPE) != 0) + return PyType_GetSlot(type, aSlot); + + switch (aSlot) { + case Py_tp_alloc: + return reinterpret_cast<void *>(type->tp_alloc); + case Py_tp_getattro: + return reinterpret_cast<void *>(type->tp_getattro); + case Py_tp_setattro: + return reinterpret_cast<void *>(type->tp_setattro); + case Py_tp_descr_get: + return reinterpret_cast<void *>(type->tp_descr_get); + case Py_tp_descr_set: + return reinterpret_cast<void *>(type->tp_descr_set); + case Py_tp_call: + return reinterpret_cast<void *>(type->tp_call); + case Py_tp_new: + return reinterpret_cast<void *>(type->tp_new); + case Py_tp_init: + return reinterpret_cast<void *>(type->tp_init); + case Py_tp_free: + return reinterpret_cast<void *>(type->tp_free); + } + assert(false); + return nullptr; } /*************************************************************************** @@ -937,16 +1227,16 @@ static inline void *PepType_ExTP(PyTypeObject *type, size_t size) static PyTypeObject *alias{}; const char *kind = size == sizeof(SbkObjectTypePrivate) ? "SOTP" : size == sizeof(SbkEnumTypePrivate) ? "SETP" : - size == sizeof(PySideQFlagsTypePrivate) ? "PFTP" : + size == sizeof(SbkQFlagsTypePrivate) ? "PFTP" : "unk."; fprintf(stderr, "%s:%d %p x %s s=%ld\n", __func__, __LINE__, type, kind, size); PyObject *kill{}; if (strlen(env_p) > 0) { - if (size == sizeof(PySideQFlagsTypePrivate)) { + if (size == sizeof(SbkQFlagsTypePrivate)) { if (alias == nullptr) alias = type; } - if (size != sizeof(PySideQFlagsTypePrivate)) { + if (size != sizeof(SbkQFlagsTypePrivate)) { if (type == alias) Py_INCREF(kill); } @@ -983,6 +1273,9 @@ Pep384_Init() PepFunction_TypePtr = getFunctionType(); PepStaticMethod_TypePtr = getStaticMethodType(); #endif // Py_LIMITED_API +#ifdef PYPY_VERSION + PepBuiltinMethod_TypePtr = getBuiltinMethodType(); +#endif } } // extern "C" |