diff options
Diffstat (limited to 'sources/shiboken6/libshiboken/sbktypefactory.cpp')
-rw-r--r-- | sources/shiboken6/libshiboken/sbktypefactory.cpp | 407 |
1 files changed, 407 insertions, 0 deletions
diff --git a/sources/shiboken6/libshiboken/sbktypefactory.cpp b/sources/shiboken6/libshiboken/sbktypefactory.cpp new file mode 100644 index 000000000..079548eed --- /dev/null +++ b/sources/shiboken6/libshiboken/sbktypefactory.cpp @@ -0,0 +1,407 @@ +// Copyright (C) 2021 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 + +#include "sbktypefactory.h" +#include "shiboken.h" + +extern "C" +{ + +using Shiboken::AutoDecRef; + +PyTypeObject *SbkType_FromSpec(PyType_Spec *spec) +{ + return SbkType_FromSpec_BMDWB(spec, nullptr, nullptr, 0, 0, nullptr); +} + +PyTypeObject *SbkType_FromSpecWithMeta(PyType_Spec *spec, PyTypeObject *meta) +{ + return SbkType_FromSpec_BMDWB(spec, nullptr, meta, 0, 0, nullptr); +} + +PyTypeObject *SbkType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases) +{ + return SbkType_FromSpec_BMDWB(spec, bases, nullptr, 0, 0, nullptr); +} + +PyTypeObject *SbkType_FromSpecBasesMeta(PyType_Spec *spec, PyObject *bases, PyTypeObject *meta) +{ + return SbkType_FromSpec_BMDWB(spec, bases, meta, 0, 0, nullptr); +} + +#ifdef PYPY_VERSION + +static PyObject *_PyType_FromSpecWithBases(PyType_Spec *, PyObject *); + +#else + +#define _PyType_FromSpecWithBases PyType_FromSpecWithBases + +#endif // PYPY_VERSION + +// PYSIDE-2230: Not so temporary fix for Python 3.12. +// A tp_new is no longer allowed in a meta class. +// Hopefully, the Python devs will supply the missing support. +// It turned out that they will not fix that, as expected. +// Note: Python 3.12 is the first version that grabs the metaclass from base classes. +static PyObject *_PyType_FromSpecWithBasesHack(PyType_Spec *spec, + PyObject *bases, + PyTypeObject *meta) +{ + PyTypeObject *keepMeta{}; + newfunc keepNew{}; + AutoDecRef basesPatch{}; + + if (bases) { + if (bases == Py_None) { + // PYSIDE-2230: This is the SbkObject entry which has no base to provide + // the metaclass. We patch it in by modifying `object`s class. + assert(meta); + auto *base = reinterpret_cast<PyObject *>(&PyBaseObject_Type); + base->ob_type = meta; + basesPatch.reset(Py_BuildValue("(O)", &PyBaseObject_Type)); + bases = basesPatch.object(); + } + + Py_ssize_t n = PyTuple_GET_SIZE(bases); + for (auto idx = 0; idx < n; ++idx) { + PyTypeObject *base = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(bases, idx)); + PyTypeObject *meta = Py_TYPE(base); + if (meta->tp_new != PyType_Type.tp_new) { + // make sure there is no second meta class + assert(keepMeta == nullptr); + keepMeta = meta; + keepNew = meta->tp_new; + meta->tp_new = PyType_Type.tp_new; + } + } + } + +#if !defined(Py_LIMITED_API) && PY_VERSION_HEX >= 0x030C0000 + auto *ret = PyType_FromMetaclass(meta, nullptr /*module*/, spec, bases); +#else + auto *ret = _PyType_FromSpecWithBases(spec, bases); +#endif + + if (keepMeta) + keepMeta->tp_new = keepNew; + if (basesPatch.object()) { + // undo the metaclass patch. + auto *base = PyTuple_GET_ITEM(basesPatch.object(), 0); + base->ob_type = &PyType_Type; + } + return ret; +} + +PyTypeObject *SbkType_FromSpec_BMDWB(PyType_Spec *spec, + PyObject *bases, + PyTypeObject *meta, + int dictoffset, + int weaklistoffset, + PyBufferProcs *bufferprocs) +{ + // PYSIDE-1286: Generate correct __module__ and __qualname__ + // The name field can now be extended by an "n:" prefix which is + // the number of modules in the name. The default is 1. + // + // Example: + // "2:mainmod.submod.mainclass.subclass" + // results in + // __module__ : "mainmod.submod" + // __qualname__ : "mainclass.subclass" + // __name__ : "subclass" + + PyType_Spec new_spec = *spec; + const char *colon = strchr(spec->name, ':'); + assert(colon); + int package_level = atoi(spec->name); + const char *mod = new_spec.name = colon + 1; + + PyObject *obType = _PyType_FromSpecWithBasesHack(&new_spec, bases, meta); + if (obType == nullptr) + return nullptr; + + const char *qual = mod; + for (int idx = package_level; idx > 0; --idx) { + const char *dot = strchr(qual, '.'); + if (!dot) + break; + qual = dot + 1; + } + int mlen = qual - mod - 1; + AutoDecRef module(Shiboken::String::fromCString(mod, mlen)); + AutoDecRef qualname(Shiboken::String::fromCString(qual)); + + auto *type = reinterpret_cast<PyTypeObject *>(obType); + + if (meta) { + PyTypeObject *hold = Py_TYPE(type); + obType->ob_type = meta; + Py_INCREF(Py_TYPE(type)); + if (hold->tp_flags & Py_TPFLAGS_HEAPTYPE) + Py_DECREF(hold); + } + + if (dictoffset) + type->tp_dictoffset = dictoffset; + if (weaklistoffset) + type->tp_weaklistoffset = weaklistoffset; + if (bufferprocs) + PepType_AS_BUFFER(type) = bufferprocs; + +#ifdef PYPY_VERSION + // PYSIDE-535: Careful: Using PyObject_SetAttr would have the side-effect of calling + // PyType_Ready too early. (at least in PyPy, which caused pretty long debugging.) + auto *ht = reinterpret_cast<PyHeapTypeObject *>(type); + ht->ht_qualname = qualname; + AutoDecRef tpDict(PepType_GetDict(type)); + if (PyDict_SetItem(tpDict.object(), Shiboken::PyMagicName::qualname(), qualname)) + return nullptr; + if (PyDict_SetItem(tpDict.object(), Shiboken::PyMagicName::module(), module)) + return nullptr; + PyType_Ready(type); +#else + if (PyObject_SetAttr(obType, Shiboken::PyMagicName::module(), module) < 0) + return nullptr; + if (PyObject_SetAttr(obType, Shiboken::PyMagicName::qualname(), qualname) < 0) + return nullptr; + PyType_Modified(type); +#endif + return type; +} + +#ifdef PYPY_VERSION + +///////////////////////////////////////////////////////////////////////////// +// +// Reimplementation of `PyType_FromSpecWithBases` +// +// This is almost the original code from Python 3.7 with a few changes. +// Especially the call to `PyType_Ready` is deferred until the needed +// post-actions are carried out in `SbkType_FromSpec_BMDWBD`. +// +// FIXME remove ASAP. +// Version is not clear, yet. Current version == 7.3.6 +// + +static const short slotoffsets[] = { + -1, /* invalid slot */ +/* Generated by typeslots.py */ +0, +0, +offsetof(PyHeapTypeObject, as_mapping.mp_ass_subscript), +offsetof(PyHeapTypeObject, as_mapping.mp_length), +offsetof(PyHeapTypeObject, as_mapping.mp_subscript), +offsetof(PyHeapTypeObject, as_number.nb_absolute), +offsetof(PyHeapTypeObject, as_number.nb_add), +offsetof(PyHeapTypeObject, as_number.nb_and), +offsetof(PyHeapTypeObject, as_number.nb_bool), +offsetof(PyHeapTypeObject, as_number.nb_divmod), +offsetof(PyHeapTypeObject, as_number.nb_float), +offsetof(PyHeapTypeObject, as_number.nb_floor_divide), +offsetof(PyHeapTypeObject, as_number.nb_index), +offsetof(PyHeapTypeObject, as_number.nb_inplace_add), +offsetof(PyHeapTypeObject, as_number.nb_inplace_and), +offsetof(PyHeapTypeObject, as_number.nb_inplace_floor_divide), +offsetof(PyHeapTypeObject, as_number.nb_inplace_lshift), +offsetof(PyHeapTypeObject, as_number.nb_inplace_multiply), +offsetof(PyHeapTypeObject, as_number.nb_inplace_or), +offsetof(PyHeapTypeObject, as_number.nb_inplace_power), +offsetof(PyHeapTypeObject, as_number.nb_inplace_remainder), +offsetof(PyHeapTypeObject, as_number.nb_inplace_rshift), +offsetof(PyHeapTypeObject, as_number.nb_inplace_subtract), +offsetof(PyHeapTypeObject, as_number.nb_inplace_true_divide), +offsetof(PyHeapTypeObject, as_number.nb_inplace_xor), +offsetof(PyHeapTypeObject, as_number.nb_int), +offsetof(PyHeapTypeObject, as_number.nb_invert), +offsetof(PyHeapTypeObject, as_number.nb_lshift), +offsetof(PyHeapTypeObject, as_number.nb_multiply), +offsetof(PyHeapTypeObject, as_number.nb_negative), +offsetof(PyHeapTypeObject, as_number.nb_or), +offsetof(PyHeapTypeObject, as_number.nb_positive), +offsetof(PyHeapTypeObject, as_number.nb_power), +offsetof(PyHeapTypeObject, as_number.nb_remainder), +offsetof(PyHeapTypeObject, as_number.nb_rshift), +offsetof(PyHeapTypeObject, as_number.nb_subtract), +offsetof(PyHeapTypeObject, as_number.nb_true_divide), +offsetof(PyHeapTypeObject, as_number.nb_xor), +offsetof(PyHeapTypeObject, as_sequence.sq_ass_item), +offsetof(PyHeapTypeObject, as_sequence.sq_concat), +offsetof(PyHeapTypeObject, as_sequence.sq_contains), +offsetof(PyHeapTypeObject, as_sequence.sq_inplace_concat), +offsetof(PyHeapTypeObject, as_sequence.sq_inplace_repeat), +offsetof(PyHeapTypeObject, as_sequence.sq_item), +offsetof(PyHeapTypeObject, as_sequence.sq_length), +offsetof(PyHeapTypeObject, as_sequence.sq_repeat), +offsetof(PyHeapTypeObject, ht_type.tp_alloc), +offsetof(PyHeapTypeObject, ht_type.tp_base), +offsetof(PyHeapTypeObject, ht_type.tp_bases), +offsetof(PyHeapTypeObject, ht_type.tp_call), +offsetof(PyHeapTypeObject, ht_type.tp_clear), +offsetof(PyHeapTypeObject, ht_type.tp_dealloc), +offsetof(PyHeapTypeObject, ht_type.tp_del), +offsetof(PyHeapTypeObject, ht_type.tp_descr_get), +offsetof(PyHeapTypeObject, ht_type.tp_descr_set), +offsetof(PyHeapTypeObject, ht_type.tp_doc), +offsetof(PyHeapTypeObject, ht_type.tp_getattr), +offsetof(PyHeapTypeObject, ht_type.tp_getattro), +offsetof(PyHeapTypeObject, ht_type.tp_hash), +offsetof(PyHeapTypeObject, ht_type.tp_init), +offsetof(PyHeapTypeObject, ht_type.tp_is_gc), +offsetof(PyHeapTypeObject, ht_type.tp_iter), +offsetof(PyHeapTypeObject, ht_type.tp_iternext), +offsetof(PyHeapTypeObject, ht_type.tp_methods), +offsetof(PyHeapTypeObject, ht_type.tp_new), +offsetof(PyHeapTypeObject, ht_type.tp_repr), +offsetof(PyHeapTypeObject, ht_type.tp_richcompare), +offsetof(PyHeapTypeObject, ht_type.tp_setattr), +offsetof(PyHeapTypeObject, ht_type.tp_setattro), +offsetof(PyHeapTypeObject, ht_type.tp_str), +offsetof(PyHeapTypeObject, ht_type.tp_traverse), +offsetof(PyHeapTypeObject, ht_type.tp_members), +offsetof(PyHeapTypeObject, ht_type.tp_getset), +offsetof(PyHeapTypeObject, ht_type.tp_free), +offsetof(PyHeapTypeObject, as_number.nb_matrix_multiply), +offsetof(PyHeapTypeObject, as_number.nb_inplace_matrix_multiply), +offsetof(PyHeapTypeObject, as_async.am_await), +offsetof(PyHeapTypeObject, as_async.am_aiter), +offsetof(PyHeapTypeObject, as_async.am_anext), +offsetof(PyHeapTypeObject, ht_type.tp_finalize), +}; + +static PyTypeObject * +best_base(PyObject *bases) +{ + // We always have only one base + return reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(bases, 0)); +} + +static PyObject * +_PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases) +{ + PyHeapTypeObject *res = reinterpret_cast<PyHeapTypeObject *>( + PyType_GenericAlloc(&PyType_Type, 0)); + PyTypeObject *type, *base; + PyObject *modname; + char *s; + char *res_start = reinterpret_cast<char *>(res); + PyType_Slot *slot; + + if (res == nullptr) + return nullptr; + + if (spec->name == nullptr) { + PyErr_SetString(PyExc_SystemError, + "Type spec does not define the name field."); + goto fail; + } + + /* Set the type name and qualname */ + s = strrchr(const_cast<char *>(spec->name), '.'); + if (s == nullptr) + s = (char*)spec->name; + else + s++; + + type = &res->ht_type; + /* The flags must be initialized early, before the GC traverses us */ + type->tp_flags = spec->flags | Py_TPFLAGS_HEAPTYPE; + res->ht_name = PyUnicode_FromString(s); + if (!res->ht_name) + goto fail; + res->ht_qualname = res->ht_name; + Py_INCREF(res->ht_qualname); + type->tp_name = spec->name; + + /* Adjust for empty tuple bases */ + if (!bases) { + base = &PyBaseObject_Type; + /* See whether Py_tp_base(s) was specified */ + for (slot = spec->slots; slot->slot; slot++) { + if (slot->slot == Py_tp_base) + base = reinterpret_cast<PyTypeObject *>(slot->pfunc); + else if (slot->slot == Py_tp_bases) { + bases = reinterpret_cast<PyObject *>(slot->pfunc); + Py_INCREF(bases); + } + } + if (!bases) + bases = PyTuple_Pack(1, base); + if (!bases) + goto fail; + } + else + Py_INCREF(bases); + + /* Calculate best base, and check that all bases are type objects */ + base = best_base(bases); + if (base == nullptr) { + goto fail; + } + + /* Initialize essential fields */ + type->tp_as_async = &res->as_async; + type->tp_as_number = &res->as_number; + type->tp_as_sequence = &res->as_sequence; + type->tp_as_mapping = &res->as_mapping; + type->tp_as_buffer = &res->as_buffer; + /* Set tp_base and tp_bases */ + type->tp_bases = bases; + bases = nullptr; + Py_INCREF(base); + type->tp_base = base; + + type->tp_basicsize = spec->basicsize; + type->tp_itemsize = spec->itemsize; + + for (slot = spec->slots; slot->slot; slot++) { + if (slot->slot == Py_tp_base || slot->slot == Py_tp_bases) + /* Processed above */ + continue; + *reinterpret_cast<void **>(res_start + slotoffsets[slot->slot]) = slot->pfunc; + + /* need to make a copy of the docstring slot, which usually + points to a static string literal */ + if (slot->slot == Py_tp_doc) { + const char *old_doc = reinterpret_cast<char *>(slot->pfunc); + //_PyType_DocWithoutSignature(type->tp_name, slot->pfunc); + size_t len = strlen(old_doc)+1; + char *tp_doc = reinterpret_cast<char *>(PyObject_MALLOC(len)); + if (tp_doc == nullptr) { + type->tp_doc = nullptr; + PyErr_NoMemory(); + goto fail; + } + memcpy(tp_doc, old_doc, len); + type->tp_doc = tp_doc; + } + } + if (type->tp_dealloc == nullptr) { + /* It's a heap type, so needs the heap types' dealloc. + subtype_dealloc will call the base type's tp_dealloc, if + necessary. */ + type->tp_dealloc = _PyPy_subtype_dealloc; + } + + /// Here is the only change needed: Do not finalize type creation. + // if (PyType_Ready(type) < 0) + // goto fail; + PepType_SetDict(type, PyDict_New()); + /// This is not found in PyPy: + // if (type->tp_dictoffset) { + // res->ht_cached_keys = _PyDict_NewKeysForClass(); + // } + + /* Set type.__module__ */ + /// Removed __module__ handling, already implemented. + + return (PyObject*)res; + + fail: + Py_DECREF(res); + return nullptr; +} + +#endif // PYPY_VERSION + +} //extern "C" |