// Copyright (C) 2018 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 // There is a bug in Python 3.6 that turned the Index_Check function // into a macro without taking care of the limited API. // This leads to the single problem that we don't have // access to PyLong_Type's nb_index field which is no heap type. // We cannot easily create this function by inheritance since it is // not inherited. // // Simple solution: Create the structure and write such a function. // Long term: Submit a patch to python.org . // This structure comes from Python 3.7, but we have checked that // it also works for Python 3.8 and 3.9. typedef struct { /* Number implementations must check *both* arguments for proper type and implement the necessary conversions in the slot functions themselves. */ binaryfunc nb_add; binaryfunc nb_subtract; binaryfunc nb_multiply; binaryfunc nb_remainder; binaryfunc nb_divmod; ternaryfunc nb_power; unaryfunc nb_negative; unaryfunc nb_positive; unaryfunc nb_absolute; inquiry nb_bool; unaryfunc nb_invert; binaryfunc nb_lshift; binaryfunc nb_rshift; binaryfunc nb_and; binaryfunc nb_xor; binaryfunc nb_or; unaryfunc nb_int; void *nb_reserved; /* the slot formerly known as nb_long */ unaryfunc nb_float; binaryfunc nb_inplace_add; binaryfunc nb_inplace_subtract; binaryfunc nb_inplace_multiply; binaryfunc nb_inplace_remainder; ternaryfunc nb_inplace_power; binaryfunc nb_inplace_lshift; binaryfunc nb_inplace_rshift; binaryfunc nb_inplace_and; binaryfunc nb_inplace_xor; binaryfunc nb_inplace_or; binaryfunc nb_floor_divide; binaryfunc nb_true_divide; binaryfunc nb_inplace_floor_divide; binaryfunc nb_inplace_true_divide; unaryfunc nb_index; binaryfunc nb_matrix_multiply; binaryfunc nb_inplace_matrix_multiply; } PyNumberMethods; // temporary structure until we have a generator for the offsets typedef struct _oldtypeobject { PyVarObject ob_base; void *X01; // const char *tp_name; void *X02; // Py_ssize_t tp_basicsize; void *X03; // Py_ssize_t tp_itemsize; void *X04; // destructor tp_dealloc; void *X05; // printfunc tp_print; void *X06; // getattrfunc tp_getattr; void *X07; // setattrfunc tp_setattr; void *X08; // PyAsyncMethods *tp_as_async; void *X09; // reprfunc tp_repr; PyNumberMethods *tp_as_number; } PyOldTypeObject; static bool is_compatible_version() { auto *sysmodule = PyImport_AddModule("sys"); auto *dic = PyModule_GetDict(sysmodule); auto *version = PyDict_GetItemString(dic, "version_info"); auto *major = PyTuple_GetItem(version, 0); auto *minor = PyTuple_GetItem(version, 1); auto number = PyLong_AsLong(major) * 1000 + PyLong_AsLong(minor); return number < 3010; } /////////////////////////////////////////////////////////////////////// // // PYSIE-1797: The Solution // ======================== // // Inspecting the data structures of Python 3.6, 3.7, 3.8 and 3.9 // shows that concerning the here needed offset of nb_index, they // are all compatible. // That means: We can use the above definition for all these versions. // // From Python 3.10 on, the `PyType_GetSlot` function also works with // non-heap types. That means this solution will always work. // // Note: When we have moved to Python 3.8 as the minimum version, // this whole nonsense can be trashed. // There is an automatic warning about this in parser.py . // LIBSHIBOKEN_API int PepIndex_Check(PyObject *obj) { static bool old_python_version = is_compatible_version(); if (old_python_version) { auto *type = reinterpret_cast(Py_TYPE(obj)); return type->tp_as_number != nullptr && type->tp_as_number->nb_index != nullptr; } // From Python 3.10 on, we can use PyType_GetSlot also with normal types! unaryfunc nb_index = reinterpret_cast(PyType_GetSlot(Py_TYPE(obj), Py_nb_index)); return nb_index != nullptr; }