// 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(&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(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; } } } auto *ret = _PyType_FromSpecWithBases(spec, bases); 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(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(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(PyTuple_GET_ITEM(bases, 0)); } static PyObject * _PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases) { PyHeapTypeObject *res = reinterpret_cast( PyType_GenericAlloc(&PyType_Type, 0)); PyTypeObject *type, *base; PyObject *modname; char *s; char *res_start = reinterpret_cast(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(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(slot->pfunc); else if (slot->slot == Py_tp_bases) { bases = reinterpret_cast(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(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(slot->pfunc); //_PyType_DocWithoutSignature(type->tp_name, slot->pfunc); size_t len = strlen(old_doc)+1; char *tp_doc = reinterpret_cast(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"