diff options
Diffstat (limited to 'sources/shiboken6/libshiboken/pep384impl.cpp')
-rw-r--r-- | sources/shiboken6/libshiboken/pep384impl.cpp | 698 |
1 files changed, 580 insertions, 118 deletions
diff --git a/sources/shiboken6/libshiboken/pep384impl.cpp b/sources/shiboken6/libshiboken/pep384impl.cpp index a4a272c2e..2b04af857 100644 --- a/sources/shiboken6/libshiboken/pep384impl.cpp +++ b/sources/shiboken6/libshiboken/pep384impl.cpp @@ -1,54 +1,25 @@ -/**************************************************************************** -** -** 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" #include "sbkstaticstrings.h" #include "sbkstaticstrings_p.h" -#include <stdlib.h> +#include "basewrapper.h" +#include "basewrapper_p.h" +#include "sbkenum.h" +#include "voidptr.h" +#include <cstdlib> +#include <cstring> 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 @@ -66,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) @@ -134,18 +105,20 @@ static PyType_Spec typeprobe_spec = { static void check_PyTypeObject_valid() { - auto *obtype = reinterpret_cast<PyObject *>(&PyType_Type); - auto *probe_tp_base = reinterpret_cast<PyTypeObject *>( - PyObject_GetAttr(obtype, Shiboken::PyMagicName::base())); + auto *typetype = &PyType_Type; + auto *obtype = reinterpret_cast<PyObject *>(typetype); + auto *probe_tp_base_obj = PyObject_GetAttr(obtype, Shiboken::PyMagicName::base()); + auto *probe_tp_base = reinterpret_cast<PyTypeObject *>(probe_tp_base_obj); auto *probe_tp_bases = PyObject_GetAttr(obtype, Shiboken::PyMagicName::bases()); - auto *check = reinterpret_cast<PyTypeObject *>( - PyType_FromSpecWithBases(&typeprobe_spec, probe_tp_bases)); - auto *typetype = reinterpret_cast<PyTypeObject *>(obtype); + auto *checkObj = PyType_FromSpecWithBases(&typeprobe_spec, probe_tp_bases); + auto *check = reinterpret_cast<PyTypeObject *>(checkObj); PyObject *w = PyObject_GetAttr(obtype, Shiboken::PyMagicName::weakrefoffset()); long probe_tp_weakrefoffset = PyLong_AsLong(w); 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 @@ -162,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 @@ -176,18 +149,14 @@ check_PyTypeObject_valid() || probe_tp_mro != typetype->tp_mro || Py_TPFLAGS_DEFAULT != (check->tp_flags & Py_TPFLAGS_DEFAULT)) Py_FatalError("The structure of type objects has changed!"); - Py_DECREF(check); - Py_DECREF(probe_tp_base); + Py_DECREF(checkObj); + Py_DECREF(probe_tp_base_obj); Py_DECREF(w); Py_DECREF(d); Py_DECREF(probe_tp_bases); Py_DECREF(probe_tp_mro); } -#if PY_VERSION_HEX < PY_ISSUE33738_SOLVED -#include "pep384_issue33738.cpp" -#endif - #endif // Py_LIMITED_API /***************************************************************************** @@ -209,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; @@ -223,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()) { @@ -278,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(); @@ -314,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 * */ @@ -391,6 +482,22 @@ Pep_GetVerboseFlag() } #endif // Py_LIMITED_API +// Support for pyerrors.h + +#if defined(Py_LIMITED_API) || PY_VERSION_HEX < 0x030C0000 +// Emulate PyErr_GetRaisedException() using the deprecated PyErr_Fetch()/PyErr_Store() +PyObject *PepErr_GetRaisedException() +{ + PyObject *type{}; + PyObject *value{}; + PyObject *traceback{}; + PyErr_Fetch(&type, &value, &traceback); + Py_XINCREF(value); + PyErr_Restore(type, value, traceback); + return value; +} +#endif // Limited or < 3.12 + /***************************************************************************** * * Support for code.h @@ -413,8 +520,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 @@ -615,11 +738,8 @@ PyTypeObject *PepStaticMethod_TypePtr = nullptr; static PyTypeObject * getStaticMethodType(void) { - // this works for Python 3, only - // "StaticMethodType = type(str.__dict__['maketrans'])\n"; static const char prog[] = - "from xxsubtype import spamlist\n" - "result = type(spamlist.__dict__['staticmeth'])\n"; + "result = type(str.__dict__['maketrans'])\n"; return reinterpret_cast<PyTypeObject *>(PepRun_GetResult(prog)); } @@ -642,6 +762,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 @@ -655,18 +798,43 @@ const char * PepType_GetNameStr(PyTypeObject *type) { const char *ret = type->tp_name; - const char *nodots = strrchr(ret, '.'); + const char *nodots = std::strrchr(ret, '.'); if (nodots) ret = nodots + 1; 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) @@ -692,7 +860,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. @@ -717,6 +885,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 @@ -752,14 +926,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); @@ -777,18 +951,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 } /***************************************************************************** @@ -810,6 +983,292 @@ 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 + * + * This has the nice side effect of a more clean implementation, + * and we don't keep the old macro version. + * + */ + +/////////////////////////////////////////////////////////////////////// +// +// 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); +} +// +/////////////////////////////////////////////////////////////////////// + +/* + * PyTypeObject extender + */ + +static std::unordered_map<PyTypeObject *, SbkObjectTypePrivate > SOTP_extender{}; +static thread_local PyTypeObject *SOTP_key{}; +static thread_local SbkObjectTypePrivate *SOTP_value{}; + +SbkObjectTypePrivate *PepType_SOTP(PyTypeObject *type) +{ + 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(type); + if (it == SOTP_extender.end()) { + it = SOTP_extender.insert({type, {}}).first; + memset(&it->second, 0, sizeof(SbkObjectTypePrivate)); + } + SOTP_key = type; + SOTP_value = &it->second; + return SOTP_value; +} + +void PepType_SOTP_delete(PyTypeObject *type) +{ + 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 + */ +static std::unordered_map<SbkEnumType *, SbkEnumTypePrivate> SETP_extender{}; +static thread_local SbkEnumType *SETP_key{}; +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); + if (it == SETP_extender.end()) { + it = SETP_extender.insert({enumType, {}}).first; + memset(&it->second, 0, sizeof(SbkEnumTypePrivate)); + } + SETP_key = enumType; + SETP_value = &it->second; + return SETP_value; +} + +void PepType_SETP_delete(SbkEnumType *enumType) +{ + SETP_extender.erase(enumType); + SETP_key = nullptr; +} + +#ifdef Py_LIMITED_API +static PyObject *emulatePyType_GetDict(PyTypeObject *type) +{ + if (_PepRuntimeVersion() < 0x030C00 || type->tp_dict) { + auto *res = type->tp_dict; + Py_XINCREF(res); + return res; + } + // 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 +} + +int PepType_SetDict(PyTypeObject *type, PyObject *dict) +{ + 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; +} + +/*************************************************************************** + * + * PYSIDE-535: The enum/flag error + * ------------------------------- + * + * This is a fragment of the code which was used to find the enum/flag + * alias error. See the change to `setTypeConverter` in sbkenum.cpp . + * + +Usage: + +python3 -c "from PySide6 import QtCore" 2>&1 | python3 tools/debug_renamer.py | uniq -c | head -10 + + 5 PepType_ExTP:940 x_A SOTP s=96 + 4 PepType_ExTP:940 x_B SETP s=24 + 2 PepType_ExTP:940 x_C PFTP s=16 + 4 PepType_ExTP:940 x_D SETP s=24 + 1 PepType_ExTP:940 x_C SETP s=24 + 2 PepType_ExTP:940 x_E PFTP s=16 + 4 PepType_ExTP:940 x_F SETP s=24 + 1 PepType_ExTP:940 x_E SETP s=24 + 4 PepType_ExTP:940 x_G SETP s=24 + 4 PepType_ExTP:940 x_H SETP s=24 + +static inline void *PepType_ExTP(PyTypeObject *type, size_t size) +{ + static const char *env_p = std::getenv("PFTP"); + if (env_p) { + static PyTypeObject *alias{}; + const char *kind = size == sizeof(SbkObjectTypePrivate) ? "SOTP" : + size == sizeof(SbkEnumTypePrivate) ? "SETP" : + 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(SbkQFlagsTypePrivate)) { + if (alias == nullptr) + alias = type; + } + if (size != sizeof(SbkQFlagsTypePrivate)) { + if (type == alias) + Py_INCREF(kill); + } + } + } + const auto ikey = reinterpret_cast<std::uintptr_t>(type); + if (ikey == cached_key) + return cached_value; + auto it = SOTP_extender.find(ikey); + if (it == SOTP_extender.end()) { + PepType_ExTP_init(type, size); + return PepType_ExTP(type, size); + } + cached_key = ikey; + cached_value = reinterpret_cast<void *>(it->second); + return cached_value; +} +*/ + /***************************************************************************** * * Module Initialization @@ -827,6 +1286,9 @@ Pep384_Init() PepFunction_TypePtr = getFunctionType(); PepStaticMethod_TypePtr = getStaticMethodType(); #endif // Py_LIMITED_API +#ifdef PYPY_VERSION + PepBuiltinMethod_TypePtr = getBuiltinMethodType(); +#endif } } // extern "C" |