aboutsummaryrefslogtreecommitdiffstats
path: root/sources/shiboken6/libshiboken/sbktypefactory.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sources/shiboken6/libshiboken/sbktypefactory.cpp')
-rw-r--r--sources/shiboken6/libshiboken/sbktypefactory.cpp407
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"