diff options
| author | Christian Tismer <tismer@stackless.com> | 2023-06-15 15:50:31 +0200 |
|---|---|---|
| committer | Christian Tismer <tismer@stackless.com> | 2023-06-30 10:44:58 +0200 |
| commit | 895c452fd874fe4dfeb5a44e8c959136ceedba13 (patch) | |
| tree | 9638efb0bcef53a703efcd8e5906dc9a483a6edb | |
| parent | 4a09bcbeb4257c7decedf0b4eed1f9847c91c739 (diff) | |
PyEnum: Shortcut old Enum code and generate Python enums directly
The amalgamation of old and new enums is slowly unraveling from the
inside. This meanwhile actually removes the old code.
Included a change by Friedemann to improve enum value handling.
After the signed/unsigned problem was fixed, there was only one case
left where Qt and Clang parser disagreed which could be fixed.
The final solution uses overloaded functions to generate all
necessary cases with minimal footprint in the executable.
Task-number: PYSIDE-1735
Change-Id: I3741ce8621e783a750f3c05241c916008f78f39b
Done-with: Friedemann.Kleint@qt.io (+2 squashed commits)
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io>
23 files changed, 484 insertions, 1079 deletions
diff --git a/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml b/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml index 646c2f311..b1557af87 100644 --- a/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml +++ b/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml @@ -268,7 +268,6 @@ <add-conversion type="QByteArray" file="../glue/qtcore.cpp" snippet="conversion-qbytearray"/> <add-conversion type="PyFloat" check="PyFloat_CheckExact(%in)" file="../glue/qtcore.cpp" snippet="conversion-pyfloat"/> <add-conversion type="PyLong" check="PyLong_CheckExact(%in)" file="../glue/qtcore.cpp" snippet="conversion-qlonglong"/> - <add-conversion type="SbkEnumType" file="../glue/qtcore.cpp" snippet="conversion-sbkobject"/> <add-conversion type="SbkObject" file="../glue/qtcore.cpp" snippet="conversion-sbkobject"/> <add-conversion type="PyDict" check="PyDict_CheckExact(%in)" file="../glue/qtcore.cpp" snippet="conversion-pydict"/> <add-conversion type="PyList" check="PyList_Check(%in)" file="../glue/qtcore.cpp" snippet="conversion-pylist"/> diff --git a/sources/pyside6/PySide6/glue/qtcore.cpp b/sources/pyside6/PySide6/glue/qtcore.cpp index ddb5a9ffa..f15fb6f8a 100644 --- a/sources/pyside6/PySide6/glue/qtcore.cpp +++ b/sources/pyside6/PySide6/glue/qtcore.cpp @@ -145,11 +145,7 @@ else { // @snippet metatype-from-type // @snippet metatype-from-metatype-type -Shiboken::AutoDecRef intArg; -if (usingNewEnum()) - intArg.reset(PyObject_GetAttrString(%PYARG_1, "value")); -else - intArg.reset(PyObject_CallMethod(%PYARG_1, "__int__", nullptr)); +Shiboken::AutoDecRef intArg(PyObject_GetAttrString(%PYARG_1, "value")); %0 = new %TYPE(PyLong_AsLong(intArg)); // @snippet metatype-from-metatype-type @@ -1187,7 +1183,7 @@ Py_END_ALLOW_THREADS if (atexit.isNull()) { qWarning("Module atexit not found for registering __moduleShutdown"); PyErr_Clear(); - }else{ + } else { regFunc.reset(PyObject_GetAttrString(atexit, "register")); if (regFunc.isNull()) { qWarning("Function atexit.register not found for registering __moduleShutdown"); diff --git a/sources/pyside6/PySide6/glue/qtgui.cpp b/sources/pyside6/PySide6/glue/qtgui.cpp index d610933ba..38e01f24c 100644 --- a/sources/pyside6/PySide6/glue/qtgui.cpp +++ b/sources/pyside6/PySide6/glue/qtgui.cpp @@ -313,10 +313,7 @@ if (_i < 0 || _i >= %CPPSELF.count()) { return 0; } QKeyCombination item = (*%CPPSELF)[_i]; -if (usingNewEnum()) - return %CONVERTTOPYTHON[QKeyCombination](item); -auto combined = item.toCombined(); -return %CONVERTTOPYTHON[int](combined); +return %CONVERTTOPYTHON[QKeyCombination](item); // @snippet qkeysequence-getitem // @snippet qkeysequence-repr diff --git a/sources/pyside6/libpyside/pysideqflags.cpp b/sources/pyside6/libpyside/pysideqflags.cpp index 6c0314e66..bff061bca 100644 --- a/sources/pyside6/libpyside/pysideqflags.cpp +++ b/sources/pyside6/libpyside/pysideqflags.cpp @@ -6,7 +6,6 @@ #include <autodecref.h> #include <sbkenum.h> #include <sbkconverter.h> -#include <sbkenum_p.h> extern "C" { struct SbkConverter; @@ -34,9 +33,7 @@ extern "C" { long val = 0; if (PyTuple_GET_SIZE(args)) { PyObject *arg = PyTuple_GET_ITEM(args, 0); - if (Shiboken::isShibokenEnum(arg)) {// faster call - val = Shiboken::Enum::getValue(arg); - } else if (PyNumber_Check(arg)) { + if (PyNumber_Check(arg)) { Shiboken::AutoDecRef number(PyNumber_Long(arg)); val = PyLong_AsLong(number); } else { diff --git a/sources/pyside6/libpyside/pysidesignal.cpp b/sources/pyside6/libpyside/pysidesignal.cpp index ad2a42802..fc630153c 100644 --- a/sources/pyside6/libpyside/pysidesignal.cpp +++ b/sources/pyside6/libpyside/pysidesignal.cpp @@ -927,8 +927,6 @@ QByteArray getTypeName(PyObject *obType) return QByteArrayLiteral("QVariantList"); if (type == &PyDict_Type) return QByteArrayLiteral("QVariantMap"); - if (Py_TYPE(type) == SbkEnumType_TypeF()) - return Shiboken::Enum::getCppName(type); return QByteArrayLiteral("PyObject"); } if (obType == Py_None) // Must be checked before as Shiboken::String::check accepts Py_None diff --git a/sources/pyside6/libpyside/signalmanager.cpp b/sources/pyside6/libpyside/signalmanager.cpp index aa20c23d6..d0f7fb383 100644 --- a/sources/pyside6/libpyside/signalmanager.cpp +++ b/sources/pyside6/libpyside/signalmanager.cpp @@ -20,7 +20,6 @@ #include <sbkstring.h> #include <sbkstaticstrings.h> #include <sbkerrors.h> -#include <sbkenum_p.h> #include <QtCore/QByteArrayView> #include <QtCore/QDebug> diff --git a/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp b/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp index b5f951490..7e292f9b2 100644 --- a/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp +++ b/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp @@ -914,7 +914,7 @@ std::optional<AbstractMetaEnum> typeEntry->setCodeGeneration(TypeEntry::GenerateNothing); // PYSIDE-2088, MSVC signedness issue in Qt const bool castToUnsigned = enumItem->isSigned() - && enumTypeEntry->cppType().contains(u"unsigned"); + && enumTypeEntry->cppType().contains(u"unsigned"_s); const EnumeratorList &enums = enumItem->enumerators(); for (const EnumeratorModelItem &valueItem : enums) { diff --git a/sources/shiboken6/ApiExtractor/abstractmetaenum.cpp b/sources/shiboken6/ApiExtractor/abstractmetaenum.cpp index 8947bffd5..dd208f452 100644 --- a/sources/shiboken6/ApiExtractor/abstractmetaenum.cpp +++ b/sources/shiboken6/ApiExtractor/abstractmetaenum.cpp @@ -129,6 +129,16 @@ const AbstractMetaEnumValueList &AbstractMetaEnum::values() const return d->m_enumValues; } +AbstractMetaEnumValueList AbstractMetaEnum::nonRejectedValues() const +{ + auto te = d->m_typeEntry; + AbstractMetaEnumValueList result = d->m_enumValues; + auto pred = [te](const AbstractMetaEnumValue &v) { + return te->isEnumValueRejected(v.name()); }; + result.erase(std::remove_if(result.begin(), result.end(), pred), result.end()); + return result; +} + void AbstractMetaEnum::addEnumValue(const AbstractMetaEnumValue &enumValue) { d->m_enumValues << enumValue; diff --git a/sources/shiboken6/ApiExtractor/abstractmetaenum.h b/sources/shiboken6/ApiExtractor/abstractmetaenum.h index da371b31b..b99dbee57 100644 --- a/sources/shiboken6/ApiExtractor/abstractmetaenum.h +++ b/sources/shiboken6/ApiExtractor/abstractmetaenum.h @@ -62,6 +62,7 @@ public: ~AbstractMetaEnum(); const AbstractMetaEnumValueList &values() const; + AbstractMetaEnumValueList nonRejectedValues() const; void addEnumValue(const AbstractMetaEnumValue &enumValue); std::optional<AbstractMetaEnumValue> findEnumValue(QStringView value) const; diff --git a/sources/shiboken6/generator/shiboken/cppgenerator.cpp b/sources/shiboken6/generator/shiboken/cppgenerator.cpp index 1f1c6b2fd..9f5cabd4b 100644 --- a/sources/shiboken6/generator/shiboken/cppgenerator.cpp +++ b/sources/shiboken6/generator/shiboken/cppgenerator.cpp @@ -2129,13 +2129,11 @@ void CppGenerator::writeConverterRegister(TextStream &s, const AbstractMetaClass << classContext.wrapperName() << ").name());\n"; } - s << '\n'; - if (!typeEntry->isValue() && !typeEntry->isSmartPointer()) return; // Python to C++ copy (value, not pointer neither reference) conversion. - s << "// Add Python to C++ copy (value, not pointer neither reference) conversion to type converter.\n"; + s << "\n// Add Python to C++ copy (value, not pointer neither reference) conversion to type converter.\n"; sourceTypeName = metaClass->name(); targetTypeName = sourceTypeName + u"_COPY"_s; QString toCpp = pythonToCppFunctionName(sourceTypeName, targetTypeName); @@ -3681,8 +3679,6 @@ void CppGenerator::writePythonToCppConversionFunctions(TextStream &s, QString pyTypeName = toNative.sourceTypeName(); if (pyTypeName == u"Py_None" || pyTypeName == u"PyNone") typeCheck = u"%in == Py_None"_s; - else if (pyTypeName == u"SbkEnumType") - typeCheck = u"Shiboken::isShibokenEnum(%in)"_s; else if (pyTypeName == u"SbkObject") typeCheck = u"Shiboken::Object::checkType(%in)"_s; } @@ -5665,13 +5661,14 @@ void CppGenerator::writeSignatureInfo(TextStream &s, const OverloadData &overloa } } -void CppGenerator::writeEnumsInitialization(TextStream &s, AbstractMetaEnumList &enums, - ErrorReturn errorReturn) const +void CppGenerator::writeEnumsInitialization(TextStream &s, AbstractMetaEnumList &enums) const { if (enums.isEmpty()) return; bool preambleWrittenE = false; bool preambleWrittenF = false; + bool etypeUsed = false; + for (const AbstractMetaEnum &cppEnum : std::as_const(enums)) { if (cppEnum.isPrivate()) continue; @@ -5686,12 +5683,13 @@ void CppGenerator::writeEnumsInitialization(TextStream &s, AbstractMetaEnumList preambleWrittenF = true; } ConfigurableScope configScope(s, cppEnum.typeEntry()); - writeEnumInitialization(s, cppEnum, errorReturn); + etypeUsed |= writeEnumInitialization(s, cppEnum); } + if (preambleWrittenE && !etypeUsed) + s << sbkUnusedVariableCast(u"EType"_s); } -void CppGenerator::writeEnumInitialization(TextStream &s, const AbstractMetaEnum &cppEnum, - ErrorReturn errorReturn) const +bool CppGenerator::writeEnumInitialization(TextStream &s, const AbstractMetaEnum &cppEnum) const { const auto enclosingClass = cppEnum.targetLangEnclosingClass(); const bool hasUpperEnclosingClass = enclosingClass @@ -5709,6 +5707,137 @@ void CppGenerator::writeEnumInitialization(TextStream &s, const AbstractMetaEnum s << (cppEnum.isAnonymous() ? "anonymous enum identified by enum value" : "enum"); s << " '" << cppEnum.name() << "'.\n"; + const bool isSigned = cppEnum.isSigned() + && !cppEnum.typeEntry()->cppType().contains(u"unsigned"_s); + const bool isAccessible = !avoidProtectedHack() || !cppEnum.isProtected(); + const auto enumValues = cppEnum.nonRejectedValues(); + + const QString prefix = cppEnum.name(); + + QString tmp; + if (const auto userType = cppEnum.typeEntry()->cppType(); !userType.isEmpty()) { + tmp = userType; + } else { + if (!isSigned && !cppEnum.underlyingType().contains(u"unsigned"_s)) + tmp += u"unsigned "_s; + tmp += cppEnum.underlyingType(); + } + const QString simpleIntType = getSimplifiedIntTypeName(tmp); + QStringList pythonEnumNames; + + // Create a list of values + const QString initializerValues = prefix + u"_InitializerValues"_s; + const QString initializerName = prefix + u"_Initializer"_s; + + // Build maybe array of enum names. + if (cppEnum.enumKind() != AnonymousEnum) { + s << "const char *" << initializerName << "[] = {\n" << indent; + for (const auto &enumValue : enumValues) { + QString name = mangleName(enumValue.name()); + s << '\"' << name << "\",\n"; + } + s << "nullptr};\n" << outdent; + } + + // Calculate formatting and record used number range. + int maxNameLen = 0; + unsigned long long valueMaskPos = 0; + long long valueMaskNeg = 0; + + for (const auto &enumValue : enumValues) { + QString name = mangleName(enumValue.name()); + + // calculate formatting + if (name.length() > maxNameLen) + maxNameLen = name.length(); + + // calculate used number range + QString numStr = enumValue.value().toString(); + if (numStr.startsWith(u"-"_s)) { + auto val = numStr.toLongLong(); + if (val < valueMaskNeg) + valueMaskNeg = val; + } else { + auto val = numStr.toULongLong(); + if (val > valueMaskPos) + valueMaskPos = val; + } + } + + // update signedness for the reduced number type. + const bool isSignedShort = valueMaskNeg < 0; + const QString usedIntType = calcMinimalIntTypeName(valueMaskPos, valueMaskNeg); + const int targetHexLen = calcUsedBits(valueMaskPos, valueMaskNeg) / 4; + + if (usedIntType != simpleIntType) + s << "// " << usedIntType << " used instead of " << simpleIntType << "\n"; + + // Calculating formatting columns + QString enumValuePrefix; + if (isAccessible) { + enumValuePrefix = usedIntType + u"("_s; + if (cppEnum.enclosingClass()) + enumValuePrefix += cppEnum.enclosingClass()->qualifiedCppName() + u"::"_s; + if (!cppEnum.isAnonymous()) + enumValuePrefix += cppEnum.name() + u"::"_s; + } + const int needSpace = enumValuePrefix.length() + 2; // braces + + // Build array of enum values + if (enumValues.isEmpty()) { + s << usedIntType << " *" << initializerValues << "{};\n"; + } else { + s << usedIntType << ' ' << initializerValues << "[] = {\n" << indent; + for (qsizetype idx = 0, last = enumValues.size() - 1; idx <= last; ++idx) { + const auto &enumValue = enumValues[idx]; + + QString valueStr = enumValue.value().toString(); + + QString enumValueText = enumValuePrefix; + if (isAccessible) + enumValueText += enumValue.name() + u')'; + else + enumValueText += valueStr; + + bool hasSign = valueStr.startsWith(u"-"_s); + if (hasSign) + valueStr.removeFirst(); + auto val = valueStr.toULongLong(); + QString valueHex = QString(u"0x%1"_s).arg(val, targetHexLen, 16, QChar(u'0')); + if (hasSign) + valueStr = u'-' + valueHex + u" -"_s + valueStr; + else + valueStr = u' ' + valueHex + u" "_s + valueStr; + if (idx != last) + enumValueText += u','; + int targetCol = needSpace + maxNameLen - enumValueText.length(); + s << enumValueText << QByteArray(targetCol, ' ') << " // " << valueStr << "\n"; + } + s << "};\n" << outdent; + } + + // Build initialization of anonymous enums + if (cppEnum.enumKind() == AnonymousEnum) { + int idx = 0; + for (const auto &enumValue : enumValues) { + const QString mangledName = mangleName(enumValue.name()); + const QString pyValue = initializerValues + u'[' + QString::number(idx++) + u']'; + if (enclosingClass || hasUpperEnclosingClass) { + s << "PyDict_SetItemString(reinterpret_cast<PyTypeObject *>(" + << enclosingObjectVariable + << ")->tp_dict, \"" << mangledName << "\",\n" << indent + << (isSignedShort ? "PyLong_FromLongLong" : "PyLong_FromUnsignedLongLong") << "(" + << pyValue << "));\n" << outdent; + } else { + s << "PyModule_AddObject(module, \"" << mangledName << "\",\n" << indent + << (isSignedShort ? "PyLong_FromLongLong" : "PyLong_FromUnsignedLongLong") << "(" + << pyValue << "));\n" << outdent; + } + } + } + + bool etypeUsed = false; + QString enumVarTypeObj = cpythonTypeNameExt(enumTypeEntry); if (!cppEnum.isAnonymous()) { int packageLevel = packageName().count(u'.') + 1; @@ -5725,73 +5854,14 @@ void CppGenerator::writeEnumInitialization(TextStream &s, const AbstractMetaEnum } s << "EType = Shiboken::Enum::" - << ((enclosingClass - || hasUpperEnclosingClass) ? "createScopedEnum" : "createGlobalEnum") - << '(' << enclosingObjectVariable << ',' << '\n' << indent - << '"' << cppEnum.name() << "\",\n" - << '"' << packageLevel << ':' << getClassTargetFullName(cppEnum) << "\",\n" - << '"' << cppEnum.qualifiedCppName() << '"'; - if (flags) - s << ",\nFType"; - s << ");\n" << outdent - << "if (!EType)\n" - << indent << errorReturn << outdent << '\n'; - } - - for (const AbstractMetaEnumValue &enumValue : cppEnum.values()) { - if (enumTypeEntry->isEnumValueRejected(enumValue.name())) - continue; - - QString enumValueText; - if (!avoidProtectedHack() || !cppEnum.isProtected()) { - enumValueText = cppEnum.typeEntry()->cppType(); - if (enumValueText.isEmpty()) - enumValueText = u"Shiboken::Enum::EnumValueType"_s; - enumValueText += u'('; - if (cppEnum.enclosingClass()) - enumValueText += cppEnum.enclosingClass()->qualifiedCppName() + u"::"_s; - // Fully qualify the value which is required for C++ 11 enum classes. - if (!cppEnum.isAnonymous()) - enumValueText += cppEnum.name() + u"::"_s; - enumValueText += enumValue.name(); - enumValueText += u')'; - } else { - enumValueText += enumValue.value().toString(); - } - - const QString mangledName = mangleName(enumValue.name()); - switch (cppEnum.enumKind()) { - case AnonymousEnum: - if (enclosingClass || hasUpperEnclosingClass) { - s << "{\n" << indent - << "PyObject *anonEnumItem = PyLong_FromLong(" << enumValueText << ");\n" - << "if (PyDict_SetItemString(reinterpret_cast<PyTypeObject *>(" - << enclosingObjectVariable - << ")->tp_dict, \"" << mangledName << "\", anonEnumItem) < 0)\n" - << indent << errorReturn << outdent - << "Py_DECREF(anonEnumItem);\n" << outdent - << "}\n"; - } else { - s << "if (PyModule_AddIntConstant(module, \"" << mangledName << "\", "; - s << enumValueText << ") < 0)\n" << indent << errorReturn << outdent; - } - break; - case CEnum: - case EnumClass: - s << "if (!Shiboken::Enum::createEnumItemOld(EType,\n" << indent - << "\"" << mangledName << "\", " << enumValueText << "))\n" << errorReturn - << outdent; - break; - } - } - if (cppEnum.enumKind() != AnonymousEnum) { - s << "// PYSIDE-1735: Resolving the whole enum class at the end for API compatibility.\n" - << "EType = morphLastEnumToPython();\n" - << enumVarTypeObj << " = EType;\n"; - } else { - s << "// PYSIDE-1735: Skip an Anonymous enum class for Python coercion.\n" + << "createPythonEnum" + << '(' << enclosingObjectVariable << ",\n" << indent + << '"' << packageLevel << ':' << getClassTargetFullName(cppEnum) << "\",\n" + << initializerName << ", " << initializerValues << ");\n" << outdent << enumVarTypeObj << " = EType;\n"; + etypeUsed = true; } + if (cppEnum.typeEntry()->flags()) { s << "// PYSIDE-1735: Mapping the flags class to the same enum class.\n" << cpythonTypeNameExt(cppEnum.typeEntry()->flags()) << " =\n" @@ -5803,6 +5873,8 @@ void CppGenerator::writeEnumInitialization(TextStream &s, const AbstractMetaEnum if (cppEnum.typeEntry()->flags()) s << "/flags"; s << ".\n\n"; + + return etypeUsed; } void CppGenerator::writeSignalInitialization(TextStream &s, const AbstractMetaClassCPtr &metaClass) @@ -6159,8 +6231,9 @@ void CppGenerator::writeClassRegister(TextStream &s, // Set typediscovery struct or fill the struct of another one if (needsTypeDiscoveryFunction(metaClass)) { - s << "Shiboken::ObjectType::setTypeDiscoveryFunctionV2(" << cpythonTypeName(metaClass) - << ", &" << cpythonBaseName(metaClass) << "_typeDiscovery);\n\n"; + s << "Shiboken::ObjectType::setTypeDiscoveryFunctionV2(\n" << indent + << cpythonTypeName(metaClass) + << ", &" << cpythonBaseName(metaClass) << "_typeDiscovery);" << outdent << "\n\n"; } AbstractMetaEnumList classEnums = metaClass->enums(); @@ -6170,7 +6243,7 @@ void CppGenerator::writeClassRegister(TextStream &s, s << "// Pass the ..._EnumFlagInfo to the class.\n" << "SbkObjectType_SetEnumFlagInfo(pyType, " << chopType(pyTypeName) << "_EnumFlagInfo);\n\n"; - writeEnumsInitialization(s, classEnums, ErrorReturn::Void); + writeEnumsInitialization(s, classEnums); if (metaClass->hasSignals()) writeSignalInitialization(s, metaClass); @@ -6997,7 +7070,7 @@ bool CppGenerator::finishGeneration() } } - writeEnumsInitialization(s, globalEnums, ErrorReturn::Default); + writeEnumsInitialization(s, globalEnums); s << "// Register primitive types converters.\n"; const PrimitiveTypeEntryCList &primitiveTypeList = primitiveTypes(); diff --git a/sources/shiboken6/generator/shiboken/cppgenerator.h b/sources/shiboken6/generator/shiboken/cppgenerator.h index 9f07529d9..f88c846ed 100644 --- a/sources/shiboken6/generator/shiboken/cppgenerator.h +++ b/sources/shiboken6/generator/shiboken/cppgenerator.h @@ -448,10 +448,8 @@ private: void writeRichCompareFunction(TextStream &s, const GeneratorContext &context) const; void writeSmartPointerRichCompareFunction(TextStream &s, const GeneratorContext &context) const; - void writeEnumsInitialization(TextStream &s, AbstractMetaEnumList &enums, - ErrorReturn errorReturn) const; - void writeEnumInitialization(TextStream &s, const AbstractMetaEnum &metaEnum, - ErrorReturn errorReturn) const; + void writeEnumsInitialization(TextStream &s, AbstractMetaEnumList &enums) const; + bool writeEnumInitialization(TextStream &s, const AbstractMetaEnum &metaEnum) const; static void writeSignalInitialization(TextStream &s, const AbstractMetaClassCPtr &metaClass); diff --git a/sources/shiboken6/generator/shiboken/shibokengenerator.cpp b/sources/shiboken6/generator/shiboken/shibokengenerator.cpp index fde07ef80..c9e95e2c8 100644 --- a/sources/shiboken6/generator/shiboken/shibokengenerator.cpp +++ b/sources/shiboken6/generator/shiboken/shibokengenerator.cpp @@ -1883,6 +1883,44 @@ QString ShibokenGenerator::getPrivateModuleHeaderFileName(const QString &moduleN return getModuleHeaderFileBaseName(moduleName) + QStringLiteral("_p.h"); } +QString ShibokenGenerator::getSimplifiedIntTypeName(const QString &name) + +{ + bool isSigned = !name.contains(u"unsigned"_s); + if (name.contains(u"long"_s)) { + if (name.contains(u"long long"_s)) + return isSigned ? u"int64_t"_s : u"uint64_t"_s; + return isSigned ? u"int32_t"_s : u"uint32_t"_s; + } + if (name.contains(u"short"_s)) + return isSigned ? u"int16_t"_s : u"uint16_t"_s; + if (name.contains(u"char"_s)) + return isSigned ? u"int8_t"_s : u"uint8_t"_s; + return isSigned ? u"int32_t"_s : u"uint32_t"_s; +} + +QString ShibokenGenerator::calcMinimalIntTypeName(uint64_t maxNumber, int64_t minNumber) +{ + // Calculate the really needed bits. + // Note: By the minimization, it depends on the real data if the result is + // signed or unsigned. The underlying type is no longer relevant. + assert(minNumber <= 0); + unsigned long long valueMask = maxNumber - minNumber; + int usedBits = (valueMask >> 32) ? 64 : (valueMask >> 16) ? 32 : (valueMask >> 8) ? 16 : 8; + return (minNumber < 0 ? u"int"_s : u"uint"_s) + QString::number(usedBits) + u"_t"_s; +} + +int ShibokenGenerator::calcUsedBits(uint64_t maxNumber, int64_t minNumber) +{ + // Calculate the really needed bits. + // Note: By the minimization, it depends on the real data if the result is + // signed or unsigned. The underlying type is no longer relevant. + assert(minNumber <= 0); + unsigned long long valueMask = maxNumber - minNumber; + int usedBits = (valueMask >> 32) ? 64 : (valueMask >> 16) ? 32 : (valueMask >> 8) ? 16 : 8; + return usedBits; +} + IncludeGroupList ShibokenGenerator::classIncludes(const AbstractMetaClassCPtr &metaClass) const { IncludeGroupList result; diff --git a/sources/shiboken6/generator/shiboken/shibokengenerator.h b/sources/shiboken6/generator/shiboken/shibokengenerator.h index 4189753cc..12d908ddb 100644 --- a/sources/shiboken6/generator/shiboken/shibokengenerator.h +++ b/sources/shiboken6/generator/shiboken/shibokengenerator.h @@ -272,6 +272,13 @@ protected: static QString getModuleHeaderFileName(const QString &moduleName = QString()); static QString getPrivateModuleHeaderFileName(const QString &moduleName = QString()); + /// Turn "unsigned? long? (long|int|short|char)" into "u?int(8|16|32|64)_t"" + static QString getSimplifiedIntTypeName(const QString &typeName); + + /// Determine the minimal needed data type for a given number range. + static QString calcMinimalIntTypeName(uint64_t maxNumber, int64_t minNumber); + static int calcUsedBits(uint64_t maxNumber, int64_t minNumber); + /// Includes for header (native wrapper class) or binding source QList<IncludeGroup> classIncludes(const AbstractMetaClassCPtr &metaClass) const; diff --git a/sources/shiboken6/libshiboken/CMakeLists.txt b/sources/shiboken6/libshiboken/CMakeLists.txt index 69a0af5ca..2e558a39d 100644 --- a/sources/shiboken6/libshiboken/CMakeLists.txt +++ b/sources/shiboken6/libshiboken/CMakeLists.txt @@ -70,7 +70,7 @@ sbkarrayconverter.cpp sbkarrayconverter.h sbkarrayconverter_p.h sbkcontainer.cpp sbkcontainer.h sbkconverter.cpp sbkconverter.h sbkconverter_p.h sbkcppstring.cpp sbkcppstring.h sbkcpptonumpy.h -sbkenum.cpp sbkenum.h sbkenum_p.h +sbkenum.cpp sbkenum.h sbkerrors.cpp sbkerrors.h sbkfeature_base.cpp sbkfeature_base.h sbkmodule.cpp sbkmodule.h @@ -158,7 +158,6 @@ install(FILES sbkconverter.h sbkcpptonumpy.h sbkenum.h - sbkenum_p.h sbkerrors.h sbkfeature_base.h sbkmodule.h diff --git a/sources/shiboken6/libshiboken/basewrapper.cpp b/sources/shiboken6/libshiboken/basewrapper.cpp index b01d01685..9e5d1b89e 100644 --- a/sources/shiboken6/libshiboken/basewrapper.cpp +++ b/sources/shiboken6/libshiboken/basewrapper.cpp @@ -721,9 +721,6 @@ void init() //Init private data Pep384_Init(); - if (PyType_Ready(SbkEnumType_TypeF()) < 0) - Py_FatalError("[libshiboken] Failed to initialize Shiboken.SbkEnumType metatype."); - if (PyType_Ready(SbkObjectType_TypeF()) < 0) Py_FatalError("[libshiboken] Failed to initialize Shiboken.BaseWrapperType metatype."); diff --git a/sources/shiboken6/libshiboken/pep384impl.cpp b/sources/shiboken6/libshiboken/pep384impl.cpp index e3b8904a5..9616712f6 100644 --- a/sources/shiboken6/libshiboken/pep384impl.cpp +++ b/sources/shiboken6/libshiboken/pep384impl.cpp @@ -8,7 +8,6 @@ #include "basewrapper.h" #include "basewrapper_p.h" #include "sbkenum.h" -#include "sbkenum_p.h" #include "sbkconverter.h" #include "voidptr.h" diff --git a/sources/shiboken6/libshiboken/sbkconverter.h b/sources/shiboken6/libshiboken/sbkconverter.h index c4a99a818..dd8d01379 100644 --- a/sources/shiboken6/libshiboken/sbkconverter.h +++ b/sources/shiboken6/libshiboken/sbkconverter.h @@ -7,7 +7,6 @@ #include "sbkpython.h" #include "shibokenmacros.h" #include "sbkenum.h" -#include "sbkenum_p.h" #include "basewrapper_p.h" #include <limits> diff --git a/sources/shiboken6/libshiboken/sbkenum.cpp b/sources/shiboken6/libshiboken/sbkenum.cpp index 1b83e8a84..502b6c669 100644 --- a/sources/shiboken6/libshiboken/sbkenum.cpp +++ b/sources/shiboken6/libshiboken/sbkenum.cpp @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "sbkenum.h" -#include "sbkenum_p.h" #include "sbkstring.h" #include "sbkstaticstrings.h" #include "sbkstaticstrings_p.h" @@ -16,377 +15,23 @@ #include <vector> #include <sstream> -#define SbkEnumType_Check(o) (Py_TYPE(Py_TYPE(o)) == SbkEnumType_TypeF()) -using enum_func = PyObject *(*)(PyObject *, PyObject *); - using namespace Shiboken; extern "C" { -// forward -struct lastEnumCreated; - -// forward -static PyTypeObject *recordCurrentEnum(PyObject *scopeOrModule, - const char *name, - PyTypeObject *enumType, - PyTypeObject *flagsType); - struct SbkEnumType { PyTypeObject type; }; -static void cleanupEnumTypes(); - -struct SbkEnumObject -{ - PyObject_HEAD - Enum::EnumValueType ob_value; - PyObject *ob_name; -}; - -static PyTypeObject *SbkEnum_TypeF(); // forward - -static PyObject *SbkEnumObject_repr(PyObject *self) -{ - const SbkEnumObject *enumObj = reinterpret_cast<SbkEnumObject *>(self); - auto name = Py_TYPE(self)->tp_name; - if (enumObj->ob_name) { - return String::fromFormat("%s.%s", name, PyBytes_AS_STRING(enumObj->ob_name)); - } - return String::fromFormat("%s(%ld)", name, enumObj->ob_value); -} - -static PyObject *SbkEnumObject_name(PyObject *self, void *) -{ - auto *enum_self = reinterpret_cast<SbkEnumObject *>(self); - - if (enum_self->ob_name == nullptr) - Py_RETURN_NONE; - - Py_INCREF(enum_self->ob_name); - return enum_self->ob_name; -} - -static PyObject *SbkEnum_tp_new(PyTypeObject *type, PyObject *args, PyObject *) -{ - long itemValue = 0; - if (!PyArg_ParseTuple(args, "|l:__new__", &itemValue)) - return nullptr; - - if (type == SbkEnum_TypeF()) { - PyErr_Format(PyExc_TypeError, "You cannot use %s directly", type->tp_name); - return nullptr; - } - - SbkEnumObject *self = PyObject_New(SbkEnumObject, type); - if (!self) - return nullptr; - self->ob_value = itemValue; - AutoDecRef item(Enum::getEnumItemFromValue(type, itemValue)); - self->ob_name = item.object() ? SbkEnumObject_name(item, nullptr) : nullptr; - return reinterpret_cast<PyObject *>(self); -} - -static const char *SbkEnum_SignatureStrings[] = { - "Shiboken.Enum(self,itemValue:int=0)", - nullptr}; // Sentinel - -static void enum_object_dealloc(PyObject *ob) -{ - auto *self = reinterpret_cast<SbkEnumObject *>(ob); - Py_XDECREF(self->ob_name); - Sbk_object_dealloc(ob); -} - -static PyObject *_enum_op(enum_func f, PyObject *a, PyObject *b) { - PyObject *valA = a; - PyObject *valB = b; - PyObject *result = nullptr; - bool enumA = false; - bool enumB = false; - - // We are not allowing floats - if (!PyFloat_Check(valA) && !PyFloat_Check(valB)) { - // Check if both variables are SbkEnumObject - if (SbkEnumType_Check(valA)) { - valA = PyLong_FromLong(reinterpret_cast<SbkEnumObject *>(valA)->ob_value); - enumA = true; - } - if (SbkEnumType_Check(valB)) { - valB = PyLong_FromLong(reinterpret_cast<SbkEnumObject *>(valB)->ob_value); - enumB = true; - } - } - - // Without an enum we are not supporting the operation - if (!(enumA || enumB)) { - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } - - result = f(valA, valB); - - // Decreasing the reference of the used variables a and b. - if (enumA) - Py_DECREF(valA); - if (enumB) - Py_DECREF(valB); - return result; -} - -/* Notes: - * On Py3k land we use long type when using integer numbers. However, on older - * versions of Python (version 2) we need to convert it to int type, - * respectively. - * - * Thus calling PyLong_FromLong() will result in calling PyLong_FromLong in - * Py3k. - */ -static PyObject *enum_int(PyObject *v) -{ - return PyLong_FromLong(reinterpret_cast<SbkEnumObject *>(v)->ob_value); -} - -static PyObject *enum_and(PyObject *self, PyObject *b) -{ - return _enum_op(PyNumber_And, self, b); -} - -static PyObject *enum_or(PyObject *self, PyObject *b) -{ - return _enum_op(PyNumber_Or, self, b); -} - -static PyObject *enum_xor(PyObject *self, PyObject *b) -{ - return _enum_op(PyNumber_Xor, self, b); -} - -static int enum_bool(PyObject *v) -{ - return (reinterpret_cast<SbkEnumObject *>(v)->ob_value > 0); -} - -static PyObject *enum_add(PyObject *self, PyObject *v) -{ - return _enum_op(PyNumber_Add, self, v); -} - -static PyObject *enum_subtract(PyObject *self, PyObject *v) -{ - return _enum_op(PyNumber_Subtract, self, v); -} - -static PyObject *enum_multiply(PyObject *self, PyObject *v) -{ - return _enum_op(PyNumber_Multiply, self, v); -} - -static PyObject *enum_richcompare(PyObject *self, PyObject *other, int op) -{ - PyObject *valA = self; - PyObject *valB = other; - PyObject *result = nullptr; - bool enumA = false; - bool enumB = false; - - // We are not allowing floats - if (!PyFloat_Check(valA) && !PyFloat_Check(valB)) { - - // Check if both variables are SbkEnumObject - if (SbkEnumType_Check(valA)) { - valA = PyLong_FromLong(reinterpret_cast<SbkEnumObject *>(valA)->ob_value); - enumA = true; - } - if (SbkEnumType_Check(valB)) { - valB = PyLong_FromLong(reinterpret_cast<SbkEnumObject *>(valB)->ob_value); - enumB =true; - } - } - - // Without an enum we are not supporting the operation - if (!(enumA || enumB)) { - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } - result = PyObject_RichCompare(valA, valB, op); - - // Decreasing the reference of the used variables a and b. - if (enumA) - Py_DECREF(valA); - if (enumB) - Py_DECREF(valB); - - return result; -} - -static Py_hash_t enum_hash(PyObject *pyObj) -{ - Py_hash_t val = reinterpret_cast<SbkEnumObject *>(pyObj)->ob_value; - if (val == -1) - val = -2; - return val; -} - -static PyGetSetDef SbkEnumGetSetList[] = { - {const_cast<char *>("name"), SbkEnumObject_name, nullptr, nullptr, nullptr}, - {nullptr, nullptr, nullptr, nullptr, nullptr} // Sentinel -}; - -static void SbkEnumTypeDealloc(PyObject *pyObj); -static PyTypeObject *SbkEnumTypeTpNew(PyTypeObject *metatype, PyObject *args, PyObject *kwds); - -static PyType_Slot SbkEnumType_Type_slots[] = { - {Py_tp_dealloc, reinterpret_cast<void *>(SbkEnumTypeDealloc)}, - {Py_tp_base, reinterpret_cast<void *>(&PyType_Type)}, - {Py_tp_alloc, reinterpret_cast<void *>(PyType_GenericAlloc)}, - {Py_tp_new, reinterpret_cast<void *>(SbkEnumTypeTpNew)}, - {Py_tp_free, reinterpret_cast<void *>(PyObject_GC_Del)}, - {0, nullptr} -}; - -// PYSIDE-535: The tp_itemsize field is inherited and does not need to be set. -// In PyPy, it _must_ not be set, because it would have the meaning that a -// `__len__` field must be defined. Not doing so creates a hard-to-find crash. -static PyType_Spec SbkEnumType_Type_spec = { - "1:Shiboken.EnumMeta", - 0, - 0, // sizeof(PyMemberDef), not for PyPy without a __len__ defined - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, - SbkEnumType_Type_slots, -}; - -PyTypeObject *SbkEnumType_TypeF(void) -{ - static auto *type = SbkType_FromSpec(&SbkEnumType_Type_spec); - return type; -} - -static void SbkEnumTypeDealloc(PyObject *pyObj) -{ - auto *enumType = reinterpret_cast<SbkEnumType *>(pyObj); - auto *setp = PepType_SETP(enumType); - - PyObject_GC_UnTrack(pyObj); -#ifndef Py_LIMITED_API -# if PY_VERSION_HEX >= 0x030A0000 - Py_TRASHCAN_BEGIN(pyObj, 1); -# else - Py_TRASHCAN_SAFE_BEGIN(pyObj); -# endif -#endif - if (setp->converter) - Conversions::deleteConverter(setp->converter); - PepType_SETP_delete(enumType); -#ifndef Py_LIMITED_API -# if PY_VERSION_HEX >= 0x030A0000 - Py_TRASHCAN_END; -# else - Py_TRASHCAN_SAFE_END(pyObj); -# endif -#endif - if (PepRuntime_38_flag) { - // PYSIDE-939: Handling references correctly. - // This was not needed before Python 3.8 (Python issue 35810) - Py_DECREF(Py_TYPE(pyObj)); - } -} - -PyTypeObject *SbkEnumTypeTpNew(PyTypeObject *metatype, PyObject *args, PyObject *kwds) -{ - init_enum(); - return PepType_Type_tp_new(metatype, args, kwds); -} - -} // extern "C" - -/////////////////////////////////////////////////////////////// -// -// PYSIDE-15: Pickling Support for Qt Enum objects -// This works very well and fixes the issue. -// -extern "C" { - -static PyObject *enum_unpickler = nullptr; - -// Pickling: reduce the Qt Enum object -static PyObject *enum___reduce__(PyObject *obj) -{ - init_enum(); - return Py_BuildValue("O(Ni)", - enum_unpickler, - Py_BuildValue("s", Py_TYPE(obj)->tp_name), - PyLong_AS_LONG(obj)); -} - -} // extern "C" - -namespace Shiboken { namespace Enum { - -// Unpickling: rebuild the Qt Enum object -PyObject *unpickleEnum(PyObject *enum_class_name, PyObject *value) -{ - AutoDecRef parts(PyObject_CallMethod(enum_class_name, - "split", "s", ".")); - if (parts.isNull()) - return nullptr; - PyObject *top_name = PyList_GetItem(parts, 0); // borrowed ref - if (top_name == nullptr) - return nullptr; - PyObject *module = PyImport_GetModule(top_name); - if (module == nullptr) { - PyErr_Format(PyExc_ImportError, "could not import module %.200s", - String::toCString(top_name)); - return nullptr; - } - AutoDecRef cur_thing(module); - int len = PyList_Size(parts); - for (int idx = 1; idx < len; ++idx) { - PyObject *name = PyList_GetItem(parts, idx); // borrowed ref - PyObject *thing = PyObject_GetAttr(cur_thing, name); - if (thing == nullptr) { - PyErr_Format(PyExc_ImportError, "could not import Qt Enum type %.200s", - String::toCString(enum_class_name)); - return nullptr; - } - cur_thing.reset(thing); - } - PyObject *klass = cur_thing; - return PyObject_CallFunctionObjArgs(klass, value, nullptr); -} - -int enumOption{}; - -} // namespace Enum -} // namespace Shiboken - -extern "C" { - // Initialization static bool _init_enum() { AutoDecRef shibo(PyImport_ImportModule("shiboken6.Shiboken")); - auto mod = shibo.object(); - // publish Shiboken.Enum so that the signature gets initialized - if (PyObject_SetAttrString(mod, "Enum", reinterpret_cast<PyObject *>(SbkEnum_TypeF())) < 0) - return false; - if (InitSignatureStrings(SbkEnum_TypeF(), SbkEnum_SignatureStrings) < 0) - return false; - enum_unpickler = PyObject_GetAttrString(mod, "_unpickle_enum"); - if (enum_unpickler == nullptr) - return false; - return true; + return !shibo.isNull(); } -static PyMethodDef SbkEnumObject_Methods[] = { - {"__reduce__", reinterpret_cast<PyCFunction>(enum___reduce__), - METH_NOARGS, nullptr}, - {nullptr, nullptr, 0, nullptr} // Sentinel -}; - static PyObject *PyEnumModule{}; static PyObject *PyEnumMeta{}; static PyObject *PyEnum{}; @@ -433,15 +78,14 @@ void init_enum() static bool isInitialized = false; if (isInitialized) return; - if (!(isInitialized || enum_unpickler || _init_enum())) - Py_FatalError("could not load enum pickling helper function"); - Py_AtExit(cleanupEnumTypes); + if (!(isInitialized || _init_enum())) + Py_FatalError("could not init enum"); // PYSIDE-1735: Determine whether we should use the old or the new enum implementation. static PyObject *option = PySys_GetObject("pyside6_option_python_enum"); if (!option || !PyLong_Check(option)) { PyErr_Clear(); - option = PyLong_FromLong(0); + option = PyLong_FromLong(1); } int ignoreOver{}; Enum::enumOption = PyLong_AsLongAndOverflow(option, &ignoreOver); @@ -467,460 +111,6 @@ int enumIsFlag(PyObject *ob_type) return 0; } -// PYSIDE-1735: Helper function to ask what enum we are using -bool usingNewEnum() -{ - return true; -} - -} // extern "C" - -// -/////////////////////////////////////////////////////////////// - -namespace Shiboken { - -class DeclaredEnumTypes -{ -public: - struct EnumEntry - { - char *name; // full name as allocated. type->tp_name might be a substring. - PyTypeObject *type; - }; - - DeclaredEnumTypes(const DeclaredEnumTypes &) = delete; - DeclaredEnumTypes(DeclaredEnumTypes &&) = delete; - DeclaredEnumTypes &operator=(const DeclaredEnumTypes &) = delete; - DeclaredEnumTypes &operator=(DeclaredEnumTypes &&) = delete; - - DeclaredEnumTypes(); - ~DeclaredEnumTypes(); - static DeclaredEnumTypes &instance(); - void addEnumType(const EnumEntry &e) { m_enumTypes.push_back(e); } - - void cleanup(); - -private: - std::vector<EnumEntry> m_enumTypes; -}; - -namespace Enum { - -// forward -static PyObject *newItemOld(PyTypeObject *enumType, EnumValueType itemValue, - const char *itemName); - -// forward -static PyTypeObject * newTypeWithNameOld(const char *name, - const char *cppName, - PyTypeObject *numbers_fromFlag); - -bool check(PyObject *pyObj) -{ - init_enum(); - - static PyTypeObject *meta = getPyEnumMeta(); - return Py_TYPE(Py_TYPE(pyObj)) == reinterpret_cast<PyTypeObject *>(meta); -} - -PyObject *getEnumItemFromValue(PyTypeObject *enumType, EnumValueType itemValue) -{ - init_enum(); - - auto *obEnumType = reinterpret_cast<PyObject *>(enumType); - AutoDecRef val2members(PyObject_GetAttrString(obEnumType, "_value2member_map_")); - if (val2members.isNull()) { - PyErr_Clear(); - return nullptr; - } - AutoDecRef ob_value(PyLong_FromLongLong(itemValue)); - auto *result = PyDict_GetItem(val2members, ob_value); - Py_XINCREF(result); - return result; -} - -static PyTypeObject *createEnum(const char *fullName, const char *cppName, - PyTypeObject *flagsType) -{ - init_enum(); - PyTypeObject *enumType = newTypeWithNameOld(fullName, cppName, flagsType); - if (PyType_Ready(enumType) < 0) { - Py_XDECREF(enumType); - return nullptr; - } - return enumType; -} - -PyTypeObject *createGlobalEnum(PyObject *module, const char *name, const char *fullName, - const char *cppName, PyTypeObject *flagsType) -{ - PyTypeObject *enumType = createEnum(fullName, cppName, flagsType); - if (enumType && PyModule_AddObject(module, name, reinterpret_cast<PyObject *>(enumType)) < 0) { - Py_DECREF(enumType); - return nullptr; - } - flagsType = recordCurrentEnum(module, name, enumType, flagsType); - if (flagsType && PyModule_AddObject(module, PepType_GetNameStr(flagsType), - reinterpret_cast<PyObject *>(flagsType)) < 0) { - Py_DECREF(enumType); - return nullptr; - } - return enumType; -} - -PyTypeObject *createScopedEnum(PyTypeObject *scope, const char *name, const char *fullName, - const char *cppName, PyTypeObject *flagsType) -{ - PyTypeObject *enumType = createEnum(fullName, cppName, flagsType); - if (enumType && PyDict_SetItemString(scope->tp_dict, name, - reinterpret_cast<PyObject *>(enumType)) < 0) { - Py_DECREF(enumType); - return nullptr; - } - auto *obScope = reinterpret_cast<PyObject *>(scope); - flagsType = recordCurrentEnum(obScope, name, enumType, flagsType); - if (flagsType && PyDict_SetItemString(scope->tp_dict, - PepType_GetNameStr(flagsType), - reinterpret_cast<PyObject *>(flagsType)) < 0) { - Py_DECREF(enumType); - return nullptr; - } - return enumType; -} - -static PyObject *createEnumItem(PyTypeObject *enumType, const char *itemName, - EnumValueType itemValue) -{ - init_enum(); - PyObject *enumItem = newItemOld(enumType, itemValue, itemName); - if (PyDict_SetItemString(enumType->tp_dict, itemName, enumItem) < 0) { - Py_DECREF(enumItem); - return nullptr; - } - return enumItem; -} - -bool createEnumItemOld(PyTypeObject *enumType, const char *itemName, EnumValueType itemValue) -{ - Shiboken::AutoDecRef enumItem(createEnumItem(enumType, itemName, itemValue)); - return !enumItem.isNull(); -} - -// This exists temporary as the old way to create an enum item. -// For the public interface, we use a new function -static PyObject *newItemOld(PyTypeObject *enumType, - EnumValueType itemValue, const char *itemName) -{ - bool newValue = true; - SbkEnumObject *enumObj; - if (!itemName) { - enumObj = reinterpret_cast<SbkEnumObject *>( - getEnumItemFromValue(enumType, itemValue)); - if (enumObj) - return reinterpret_cast<PyObject *>(enumObj); - - newValue = false; - } - - enumObj = PyObject_New(SbkEnumObject, enumType); - if (!enumObj) - return nullptr; - - enumObj->ob_name = itemName ? PyBytes_FromString(itemName) : nullptr; - enumObj->ob_value = itemValue; - - if (newValue) { - auto dict = enumType->tp_dict; // Note: 'values' is borrowed - PyObject *values = PyDict_GetItemWithError(dict, PyName::values()); - if (values == nullptr) { - if (PyErr_Occurred()) - return nullptr; - AutoDecRef new_values(values = PyDict_New()); - if (values == nullptr) - return nullptr; - if (PyDict_SetItem(dict, PyName::values(), values) < 0) - return nullptr; - } - PyDict_SetItemString(values, itemName, reinterpret_cast<PyObject *>(enumObj)); - } - - return reinterpret_cast<PyObject *>(enumObj); -} - -PyObject *newItem(PyTypeObject *enumType, EnumValueType itemValue, - const char *itemName) -{ - init_enum(); - - auto *obEnumType = reinterpret_cast<PyObject *>(enumType); - if (!itemName) - return PyObject_CallFunction(obEnumType, "L", itemValue); - - static PyObject *const _member_map_ = String::createStaticString("_member_map_"); - auto *member_map = PyDict_GetItem(enumType->tp_dict, _member_map_); - if (!(member_map && PyDict_Check(member_map))) - return nullptr; - auto *result = PyDict_GetItemString(member_map, itemName); - Py_XINCREF(result); - return result; -} - -} // namespace Shiboken -} // namespace Enum - -static PyType_Slot SbkNewEnum_slots[] = { - {Py_tp_repr, reinterpret_cast<void *>(SbkEnumObject_repr)}, - {Py_tp_str, reinterpret_cast<void *>(SbkEnumObject_repr)}, - {Py_tp_getset, reinterpret_cast<void *>(SbkEnumGetSetList)}, - {Py_tp_methods, reinterpret_cast<void *>(SbkEnumObject_Methods)}, - {Py_tp_new, reinterpret_cast<void *>(SbkEnum_tp_new)}, - {Py_nb_add, reinterpret_cast<void *>(enum_add)}, - {Py_nb_subtract, reinterpret_cast<void *>(enum_subtract)}, - {Py_nb_multiply, reinterpret_cast<void *>(enum_multiply)}, - {Py_nb_positive, reinterpret_cast<void *>(enum_int)}, - {Py_nb_bool, reinterpret_cast<void *>(enum_bool)}, - {Py_nb_and, reinterpret_cast<void *>(enum_and)}, - {Py_nb_xor, reinterpret_cast<void *>(enum_xor)}, - {Py_nb_or, reinterpret_cast<void *>(enum_or)}, - {Py_nb_int, reinterpret_cast<void *>(enum_int)}, - {Py_nb_index, reinterpret_cast<void *>(enum_int)}, - {Py_tp_richcompare, reinterpret_cast<void *>(enum_richcompare)}, - {Py_tp_hash, reinterpret_cast<void *>(enum_hash)}, - {Py_tp_dealloc, reinterpret_cast<void *>(enum_object_dealloc)}, - {0, nullptr} -}; -static PyType_Spec SbkNewEnum_spec = { - "1:Shiboken.Enum", - sizeof(SbkEnumObject), - 0, - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, - SbkNewEnum_slots, -}; - -static PyTypeObject *SbkEnum_TypeF() -{ - static auto type = SbkType_FromSpecWithMeta(&SbkNewEnum_spec, SbkEnumType_TypeF()); - return type; -} - -namespace Shiboken { namespace Enum { - -static void -copyNumberMethods(PyTypeObject *flagsType, - PyType_Slot number_slots[], - int *pidx) -{ - int idx = *pidx; -#define PUT_SLOT(name) \ - number_slots[idx].slot = (name); \ - number_slots[idx].pfunc = PyType_GetSlot(flagsType, (name)); \ - ++idx; - - PUT_SLOT(Py_nb_absolute); - PUT_SLOT(Py_nb_add); - PUT_SLOT(Py_nb_and); - PUT_SLOT(Py_nb_bool); - PUT_SLOT(Py_nb_divmod); - PUT_SLOT(Py_nb_float); - PUT_SLOT(Py_nb_floor_divide); - PUT_SLOT(Py_nb_index); - PUT_SLOT(Py_nb_inplace_add); - PUT_SLOT(Py_nb_inplace_and); - PUT_SLOT(Py_nb_inplace_floor_divide); - PUT_SLOT(Py_nb_inplace_lshift); - PUT_SLOT(Py_nb_inplace_multiply); - PUT_SLOT(Py_nb_inplace_or); - PUT_SLOT(Py_nb_inplace_power); - PUT_SLOT(Py_nb_inplace_remainder); - PUT_SLOT(Py_nb_inplace_rshift); - PUT_SLOT(Py_nb_inplace_subtract); - PUT_SLOT(Py_nb_inplace_true_divide); - PUT_SLOT(Py_nb_inplace_xor); - PUT_SLOT(Py_nb_int); - PUT_SLOT(Py_nb_invert); - PUT_SLOT(Py_nb_lshift); - PUT_SLOT(Py_nb_multiply); - PUT_SLOT(Py_nb_negative); - PUT_SLOT(Py_nb_or); - PUT_SLOT(Py_nb_positive); - PUT_SLOT(Py_nb_power); - PUT_SLOT(Py_nb_remainder); - PUT_SLOT(Py_nb_rshift); - PUT_SLOT(Py_nb_subtract); - PUT_SLOT(Py_nb_true_divide); - PUT_SLOT(Py_nb_xor); -#undef PUT_SLOT - *pidx = idx; -} - -static PyTypeObject * newTypeWithNameOld(const char *name, - const char *cppName, - PyTypeObject *numbers_fromFlag) -{ - // Careful: SbkType_FromSpec does not allocate the string. - PyType_Slot newslots[99] = {}; // enough but not too big for the stack - PyType_Spec newspec; - DeclaredEnumTypes::EnumEntry entry{strdup(name), nullptr}; - newspec.name = entry.name; // Note that SbkType_FromSpec might use a substring. - newspec.basicsize = SbkNewEnum_spec.basicsize; - newspec.itemsize = SbkNewEnum_spec.itemsize; - newspec.flags = SbkNewEnum_spec.flags; - // we must append all the number methods, so rebuild everything: - int idx = 0; - while (SbkNewEnum_slots[idx].slot) { - newslots[idx].slot = SbkNewEnum_slots[idx].slot; - newslots[idx].pfunc = SbkNewEnum_slots[idx].pfunc; - ++idx; - } - if (numbers_fromFlag) - copyNumberMethods(numbers_fromFlag, newslots, &idx); - newspec.slots = newslots; - AutoDecRef bases(PyTuple_New(1)); - static auto basetype = reinterpret_cast<PyObject *>(SbkEnum_TypeF()); - Py_INCREF(basetype); - PyTuple_SetItem(bases, 0, basetype); - auto *type = SbkType_FromSpecBasesMeta(&newspec, bases, SbkEnumType_TypeF()); - entry.type = type; - - auto *enumType = reinterpret_cast<SbkEnumType *>(type); - auto *setp = PepType_SETP(enumType); - setp->cppName = cppName; - DeclaredEnumTypes::instance().addEnumType(entry); - return entry.type; -} - -// PySIDE-1735: This function is in the API and should be removed in 6.4 . -// Python enums are created differently. -PyTypeObject *newTypeWithName([[maybe_unused]] const char *name, - [[maybe_unused]] const char *cppName, - [[maybe_unused]] PyTypeObject *numbers_fromFlag) -{ - // old enums are gone, remove completely? - PyErr_Format(PyExc_RuntimeError, "function `%s` can no longer be used because old " - "Enums are no longer supported", __FUNCTION__); - return nullptr; -} - -const char *getCppName(PyTypeObject *enumType) -{ - assert(Py_TYPE(enumType) == SbkEnumType_TypeF()); - auto *type = reinterpret_cast<SbkEnumType *>(enumType); - auto *setp = PepType_SETP(type); - return setp->cppName; -} - -EnumValueType getValue(PyObject *enumItem) -{ - init_enum(); - - assert(Enum::check(enumItem)); - - AutoDecRef pyValue(PyObject_GetAttrString(enumItem, "value")); - return PyLong_AsLongLong(pyValue); -} - -void setTypeConverter(PyTypeObject *type, SbkConverter *converter, bool isFlag) -{ - if (isFlag) { - auto *flagsType = reinterpret_cast<PySideQFlagsType *>(type); - PepType_PFTP(flagsType)->converter = converter; - } - else { - auto *enumType = reinterpret_cast<SbkEnumType *>(type); - PepType_SETP(enumType)->converter = converter; - } -} - -} // namespace Enum - -DeclaredEnumTypes &DeclaredEnumTypes::instance() -{ - static DeclaredEnumTypes me; - return me; -} - -DeclaredEnumTypes::DeclaredEnumTypes() = default; - -DeclaredEnumTypes::~DeclaredEnumTypes() -{ - cleanup(); -} - -void DeclaredEnumTypes::cleanup() -{ - static bool was_called = false; - if (was_called) - return; - - for (const auto &e : m_enumTypes) { - std::free(e.name); - } - m_enumTypes.clear(); - was_called = true; -} - -} // namespace Shiboken - -static void cleanupEnumTypes() -{ - DeclaredEnumTypes::instance().cleanup(); -} - -/////////////////////////////////////////////////////////////////////// -// -// PYSIDE-1735: Re-implementation of Enums using Python -// ==================================================== -// -// This is a very simple, first implementation of a replacement -// for the Qt-like Enums using the Python Enum module. -// -// The basic idea: -// --------------- -// * We create the Enums as always -// * After creation of each enum, a special function is called that -// * grabs the last generated enum -// * reads all Enum items -// * generates a class statement for the Python Enum -// * creates a new Python Enum class -// * replaces the already inserted Enum with the new one. -// -// There are lots of ways to optimize that. Will be added later. -// -extern "C" { - -struct lastEnumCreated { - PyObject *scopeOrModule; - const char *name; - PyTypeObject *enumType; - PyTypeObject *flagsType; -}; - -static lastEnumCreated lec{}; - -static PyTypeObject *recordCurrentEnum(PyObject *scopeOrModule, - const char *name, - PyTypeObject *enumType, - PyTypeObject *flagsType) -{ - lec.scopeOrModule = scopeOrModule; - lec.name = name; - lec.enumType = enumType; - lec.flagsType = flagsType; - - // We return nullptr as flagsType to disable flag creation. - return nullptr; -} - -static bool is_old_version() -{ - auto *version = PySys_GetObject("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 <= 3008; -} - /////////////////////////////////////////////////////////////////////// // // Support for Missing Values @@ -942,6 +132,7 @@ static bool is_old_version() // We create each constant only once and keep the result in a dict // "_sbk_missing_". This is similar to a competitor's "_sip_missing_". // + static PyObject *missing_func(PyObject * /* self */ , PyObject *args) { // In order to relax matters to be more compatible with C++, we need @@ -1017,16 +208,100 @@ static PyObject *create_missing_func(PyObject *klass) // //////////////////////////////////////////////////////////////////////// -PyTypeObject *morphLastEnumToPython() +} // extern "C" + +namespace Shiboken { + +namespace Enum { + +int enumOption{}; + +bool check(PyObject *pyObj) +{ + init_enum(); + + static PyTypeObject *meta = getPyEnumMeta(); + return Py_TYPE(Py_TYPE(pyObj)) == reinterpret_cast<PyTypeObject *>(meta); +} + +PyObject *getEnumItemFromValue(PyTypeObject *enumType, EnumValueType itemValue) +{ + init_enum(); + + auto *obEnumType = reinterpret_cast<PyObject *>(enumType); + AutoDecRef val2members(PyObject_GetAttrString(obEnumType, "_value2member_map_")); + if (val2members.isNull()) { + PyErr_Clear(); + return nullptr; + } + AutoDecRef ob_value(PyLong_FromLongLong(itemValue)); + auto *result = PyDict_GetItem(val2members, ob_value); + Py_XINCREF(result); + return result; +} + +PyObject *newItem(PyTypeObject *enumType, EnumValueType itemValue, + const char *itemName) +{ + init_enum(); + + auto *obEnumType = reinterpret_cast<PyObject *>(enumType); + if (!itemName) + return PyObject_CallFunction(obEnumType, "L", itemValue); + + static PyObject *const _member_map_ = String::createStaticString("_member_map_"); + auto *member_map = PyDict_GetItem(enumType->tp_dict, _member_map_); + if (!(member_map && PyDict_Check(member_map))) + return nullptr; + auto *result = PyDict_GetItemString(member_map, itemName); + Py_XINCREF(result); + return result; +} + +EnumValueType getValue(PyObject *enumItem) +{ + init_enum(); + + assert(Enum::check(enumItem)); + + AutoDecRef pyValue(PyObject_GetAttrString(enumItem, "value")); + return PyLong_AsLongLong(pyValue); +} + +void setTypeConverter(PyTypeObject *type, SbkConverter *converter, bool isFlag) +{ + if (isFlag) { + auto *flagsType = reinterpret_cast<PySideQFlagsType *>(type); + PepType_PFTP(flagsType)->converter = converter; + } + else { + auto *enumType = reinterpret_cast<SbkEnumType *>(type); + PepType_SETP(enumType)->converter = converter; + } +} + +static PyTypeObject *createEnumForPython(PyObject *scopeOrModule, + const char *fullName, + PyObject *pyEnumItems) { - /// The Python Enum internal structure is way too complicated. - /// It is much easier to generate Python code and execute it. + const char *colon = strchr(fullName, ':'); + assert(colon); + int package_level = atoi(fullName); + const char *mod = colon + 1; - // Pick up the last generated Enum and convert it into a PyEnum - auto *enumType = lec.enumType; - // This is temporary; SbkEnumType will be removed, soon. + 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)); + const char *dot = strrchr(qual, '.'); + AutoDecRef name(Shiboken::String::fromCString(dot ? dot + 1 : qual)); - auto *scopeOrModule = lec.scopeOrModule; static PyObject *enumName = String::createStaticString("IntEnum"); if (PyType_Check(scopeOrModule)) { // For global objects, we have no good solution, yet where to put the int info. @@ -1034,15 +309,9 @@ PyTypeObject *morphLastEnumToPython() auto *sotp = PepType_SOTP(type); if (!sotp->enumFlagsDict) initEnumFlagsDict(type); - enumName = PyDict_GetItem(sotp->enumTypeDict, String::fromCString(lec.name)); + enumName = PyDict_GetItem(sotp->enumTypeDict, name); } - PyObject *key, *value; - Py_ssize_t pos = 0; - PyObject *values = PyDict_GetItem(enumType->tp_dict, PyName::values()); - if (!values) - return nullptr; - AutoDecRef PyEnumType(PyObject_GetAttr(PyEnumModule, enumName)); assert(PyEnumType.object()); bool isFlag = PyObject_IsSubclass(PyEnumType, PyFlag); @@ -1055,24 +324,13 @@ PyTypeObject *morphLastEnumToPython() PyEnumType.reset(surrogate); } - // Walk the values dict and create a Python enum type. - AutoDecRef name(PyUnicode_FromString(lec.name)); - AutoDecRef args(PyList_New(0)); + // Walk the enumItemStrings and create a Python enum type. auto *pyName = name.object(); - auto *pyArgs = args.object(); - while (PyDict_Next(values, &pos, &key, &value)) { - auto *key_value = PyTuple_New(2); - PyTuple_SET_ITEM(key_value, 0, key); - Py_INCREF(key); - auto *obj = reinterpret_cast<SbkEnumObject *>(value); - auto *num = PyLong_FromLongLong(obj->ob_value); - PyTuple_SET_ITEM(key_value, 1, num); - PyList_Append(pyArgs, key_value); - } + // We now create the new type. Since Python 3.11, we need to pass in // `boundary=KEEP` because the default STRICT crashes on us. // See QDir.Filter.Drives | QDir.Filter.Files - AutoDecRef callArgs(Py_BuildValue("(OO)", pyName, pyArgs)); + AutoDecRef callArgs(Py_BuildValue("(OO)", pyName, pyEnumItems)); AutoDecRef callDict(PyDict_New()); static PyObject *boundary = String::createStaticString("boundary"); if (PyFlag_KEEP) @@ -1091,31 +349,111 @@ PyTypeObject *morphLastEnumToPython() } auto *newType = reinterpret_cast<PyTypeObject *>(obNewType); - auto *obEnumType = reinterpret_cast<PyObject *>(enumType); - AutoDecRef qual_name(PyObject_GetAttr(obEnumType, PyMagicName::qualname())); - PyObject_SetAttr(obNewType, PyMagicName::qualname(), qual_name); - AutoDecRef module(PyObject_GetAttr(obEnumType, PyMagicName::module())); + PyObject_SetAttr(obNewType, PyMagicName::qualname(), qualname); PyObject_SetAttr(obNewType, PyMagicName::module(), module); // See if we should re-introduce shortcuts in the enclosing object. const bool useGlobalShortcut = (Enum::enumOption & Enum::ENOPT_GLOBAL_SHORTCUT) != 0; const bool useScopedShortcut = (Enum::enumOption & Enum::ENOPT_SCOPED_SHORTCUT) != 0; if (useGlobalShortcut || useScopedShortcut) { + // We have to use the iterator protokol because the values dict is a mappingproxy. + AutoDecRef values(PyObject_GetAttr(obNewType, PyMagicName::members())); + AutoDecRef mapIterator(PyObject_GetIter(values)); + AutoDecRef mapKey{}; bool isModule = PyModule_Check(scopeOrModule); - pos = 0; - while (PyDict_Next(values, &pos, &key, &value)) { - AutoDecRef entry(PyObject_GetAttr(obNewType, key)); - if ((useGlobalShortcut && isModule) || (useScopedShortcut && !isModule)) - if (PyObject_SetAttr(scopeOrModule, key, entry) < 0) + while ((mapKey.reset(PyIter_Next(mapIterator))), mapKey.object()) { + if ((useGlobalShortcut && isModule) || (useScopedShortcut && !isModule)) { + AutoDecRef value(PyObject_GetItem(values, mapKey)); + if (PyObject_SetAttr(scopeOrModule, mapKey, value) < 0) return nullptr; + } } } - // PYSIDE-1735: Old Python versions can't stand the early enum deallocation. - static bool old_python_version = is_old_version(); - if (old_python_version) - Py_INCREF(obEnumType); return newType; } -} // extern "C" +template <typename IntT> +static PyObject *toPyObject(IntT v) +{ + if constexpr (sizeof(IntT) == 8) { + if constexpr (std::is_unsigned_v<IntT>) + return PyLong_FromUnsignedLongLong(v); + return PyLong_FromLongLong(v); + } + if constexpr (std::is_unsigned_v<IntT>) + return PyLong_FromUnsignedLong(v); + return PyLong_FromLong(v); +} + +template <typename IntT> +static PyTypeObject *createPythonEnumHelper(PyObject *module, + const char *fullName, const char *enumItemStrings[], IntT enumValues[]) +{ + AutoDecRef args(PyList_New(0)); + auto *pyEnumItems = args.object(); + for (size_t idx = 0; enumItemStrings[idx] != nullptr; ++idx) { + const char *kv = enumItemStrings[idx]; + auto *key = PyUnicode_FromString(kv); + auto *value = toPyObject(enumValues[idx]); + auto *key_value = PyTuple_New(2); + PyTuple_SET_ITEM(key_value, 0, key); + PyTuple_SET_ITEM(key_value, 1, value); + PyList_Append(pyEnumItems, key_value); + } + return createEnumForPython(module, fullName, pyEnumItems); +} + +// Now we have to concretize these functions explicitly, +// otherwise templates will not work across modules. + +PyTypeObject *createPythonEnum(PyObject *module, + const char *fullName, const char *enumItemStrings[], int64_t enumValues[]) +{ + return createPythonEnumHelper(module, fullName, enumItemStrings, enumValues); +} + +PyTypeObject *createPythonEnum(PyObject *module, + const char *fullName, const char *enumItemStrings[], uint64_t enumValues[]) +{ + return createPythonEnumHelper(module, fullName, enumItemStrings, enumValues); +} + +PyTypeObject *createPythonEnum(PyObject *module, + const char *fullName, const char *enumItemStrings[], int32_t enumValues[]) +{ + return createPythonEnumHelper(module, fullName, enumItemStrings, enumValues); +} + +PyTypeObject *createPythonEnum(PyObject *module, + const char *fullName, const char *enumItemStrings[], uint32_t enumValues[]) +{ + return createPythonEnumHelper(module, fullName, enumItemStrings, enumValues); +} + +PyTypeObject *createPythonEnum(PyObject *module, + const char *fullName, const char *enumItemStrings[], int16_t enumValues[]) +{ + return createPythonEnumHelper(module, fullName, enumItemStrings, enumValues); +} + +PyTypeObject *createPythonEnum(PyObject *module, + const char *fullName, const char *enumItemStrings[], uint16_t enumValues[]) +{ + return createPythonEnumHelper(module, fullName, enumItemStrings, enumValues); +} + +PyTypeObject *createPythonEnum(PyObject *module, + const char *fullName, const char *enumItemStrings[], int8_t enumValues[]) +{ + return createPythonEnumHelper(module, fullName, enumItemStrings, enumValues); +} + +PyTypeObject *createPythonEnum(PyObject *module, + const char *fullName, const char *enumItemStrings[], uint8_t enumValues[]) +{ + return createPythonEnumHelper(module, fullName, enumItemStrings, enumValues); +} + +} // namespace Enum +} // namespace Shiboken diff --git a/sources/shiboken6/libshiboken/sbkenum.h b/sources/shiboken6/libshiboken/sbkenum.h index b14e6dd42..4f21d3338 100644 --- a/sources/shiboken6/libshiboken/sbkenum.h +++ b/sources/shiboken6/libshiboken/sbkenum.h @@ -15,71 +15,89 @@ LIBSHIBOKEN_API bool PyEnumMeta_Check(PyObject *ob); /// exposed for the signature module LIBSHIBOKEN_API void init_enum(); -extern LIBSHIBOKEN_API PyTypeObject *SbkEnumType_TypeF(void); struct SbkConverter; struct SbkEnumType; -struct SbkEnumTypePrivate; -} // extern "C" - -namespace Shiboken +struct SbkEnumTypePrivate { + SbkConverter *converter; +}; + +/// PYSIDE-1735: Pass on the Python enum/flag information. +LIBSHIBOKEN_API void initEnumFlagsDict(PyTypeObject *type); + +/// PYSIDE-1735: Make sure that we can import the Python enum implementation. +LIBSHIBOKEN_API PyTypeObject *getPyEnumMeta(); +/// PYSIDE-1735: Helper function supporting QEnum +LIBSHIBOKEN_API int enumIsFlag(PyObject *ob_enum); -inline bool isShibokenEnum(PyObject *pyObj) -{ - return Py_TYPE(Py_TYPE(pyObj)) == SbkEnumType_TypeF(); } -namespace Enum +namespace Shiboken { namespace Enum { + +enum : int { + ENOPT_OLD_ENUM = 0x00, // PySide 6.6: no longer supported + ENOPT_NEW_ENUM = 0x01, + ENOPT_INHERIT_INT = 0x02, + ENOPT_GLOBAL_SHORTCUT = 0x04, + ENOPT_SCOPED_SHORTCUT = 0x08, + ENOPT_NO_FAKESHORTCUT = 0x10, + ENOPT_NO_FAKERENAMES = 0x20, + ENOPT_NO_ZERODEFAULT = 0x40, + ENOPT_NO_MISSING = 0x80, +}; + +LIBSHIBOKEN_API extern int enumOption; + +using EnumValueType = long long; + +LIBSHIBOKEN_API bool check(PyObject *obj); + +LIBSHIBOKEN_API PyObject *newItem(PyTypeObject *enumType, EnumValueType itemValue, + const char *itemName = nullptr); + +LIBSHIBOKEN_API EnumValueType getValue(PyObject *enumItem); +LIBSHIBOKEN_API PyObject *getEnumItemFromValue(PyTypeObject *enumType, + EnumValueType itemValue); + +/// Sets the enum/flag's type converter. +LIBSHIBOKEN_API void setTypeConverter(PyTypeObject *type, SbkConverter *converter, bool isFlag); + +/// Creating Python enums for different types. +LIBSHIBOKEN_API PyTypeObject *createPythonEnum(PyObject *module, + const char *fullName, const char *enumItemStrings[], int64_t enumValues[]); + +LIBSHIBOKEN_API PyTypeObject *createPythonEnum(PyObject *module, + const char *fullName, const char *enumItemStrings[], uint64_t enumValues[]); + +LIBSHIBOKEN_API PyTypeObject *createPythonEnum(PyObject *module, + const char *fullName, const char *enumItemStrings[], int32_t enumValues[]); + +LIBSHIBOKEN_API PyTypeObject *createPythonEnum(PyObject *module, + const char *fullName, const char *enumItemStrings[], uint32_t enumValues[]); + +LIBSHIBOKEN_API PyTypeObject *createPythonEnum(PyObject *module, + const char *fullName, const char *enumItemStrings[], int16_t enumValues[]); + +LIBSHIBOKEN_API PyTypeObject *createPythonEnum(PyObject *module, + const char *fullName, const char *enumItemStrings[], uint16_t enumValues[]); + +LIBSHIBOKEN_API PyTypeObject *createPythonEnum(PyObject *module, + const char *fullName, const char *enumItemStrings[], int8_t enumValues[]); + +LIBSHIBOKEN_API PyTypeObject *createPythonEnum(PyObject *module, + const char *fullName, const char *enumItemStrings[], uint8_t enumValues[]); + +/// This template removes duplication by inlining necessary type casts. +template <typename IntT> +inline PyTypeObject *createPythonEnum(PyTypeObject *scope, + const char *fullName, const char *enumItemStrings[], IntT enumValues[]) { - using EnumValueType = long long; - - LIBSHIBOKEN_API bool check(PyObject *obj); - /** - * Creates a new enum type (and its flags type, if any is given) - * and registers it to Python and adds it to \p module. - * \param module Module to where the new enum type will be added. - * \param name Name of the enum. - * \param fullName Name of the enum that includes all scope information (e.g.: "module.Enum"). - * \param cppName Full qualified C++ name of the enum. - * \param flagsType Optional Python type for the flags associated with the enum. - * \return The new enum type or NULL if it fails. - */ - LIBSHIBOKEN_API PyTypeObject *createGlobalEnum(PyObject *module, - const char *name, - const char *fullName, - const char *cppName, - PyTypeObject *flagsType = nullptr); - /// This function does the same as createGlobalEnum, but adds the enum to a Shiboken type or namespace. - LIBSHIBOKEN_API PyTypeObject *createScopedEnum(PyTypeObject *scope, - const char *name, - const char *fullName, - const char *cppName, - PyTypeObject *flagsType = nullptr); - - /// Creates a new enum item for a given enum type. - LIBSHIBOKEN_API bool createEnumItemOld(PyTypeObject *enumType, - const char *itemName, - EnumValueType itemValue); - - LIBSHIBOKEN_API PyObject *newItem(PyTypeObject *enumType, EnumValueType itemValue, - const char *itemName = nullptr); - - LIBSHIBOKEN_API PyTypeObject *newTypeWithName(const char *name, const char *cppName, - PyTypeObject *numbers_fromFlag=nullptr); - LIBSHIBOKEN_API const char *getCppName(PyTypeObject *type); - LIBSHIBOKEN_API PyObject *getCppNameNew(PyTypeObject *type); - - LIBSHIBOKEN_API EnumValueType getValue(PyObject *enumItem); - LIBSHIBOKEN_API PyObject *getEnumItemFromValue(PyTypeObject *enumType, - EnumValueType itemValue); - - /// Sets the enum/flag's type converter. - LIBSHIBOKEN_API void setTypeConverter(PyTypeObject *type, SbkConverter *converter, bool isFlag); - - LIBSHIBOKEN_API PyObject *unpickleEnum(PyObject *, PyObject *); + auto *obScope = reinterpret_cast<PyObject *>(scope); + return createPythonEnum(obScope, fullName, enumItemStrings, enumValues); } +} // namespace Enum } // namespace Shiboken #endif // SKB_PYENUM_H diff --git a/sources/shiboken6/libshiboken/sbkenum_p.h b/sources/shiboken6/libshiboken/sbkenum_p.h deleted file mode 100644 index 3e3640b93..000000000 --- a/sources/shiboken6/libshiboken/sbkenum_p.h +++ /dev/null @@ -1,51 +0,0 @@ -// 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 - -#ifndef SBKENUM_P_H -#define SBKENUM_P_H - -#include "sbkpython.h" -#include "shibokenmacros.h" - -struct SbkEnumTypePrivate -{ - SbkConverter *converter; - const char *cppName; -}; - -extern "C" { - -/// PYSIDE-1735: Pass on the Python enum/flag information. -LIBSHIBOKEN_API void initEnumFlagsDict(PyTypeObject *type); - -/// PYSIDE-1735: Patching the Enum / Flags implementation. Remove in 6.4 -LIBSHIBOKEN_API PyTypeObject *morphLastEnumToPython(); - -/// PYSIDE-1735: Make sure that we can import the Python enum implementation. -LIBSHIBOKEN_API PyTypeObject *getPyEnumMeta(); -/// PYSIDE-1735: Helper function supporting QEnum -LIBSHIBOKEN_API int enumIsFlag(PyObject *ob_enum); -/// PYSIDE-1735: Helper function to ask what enum we are using -LIBSHIBOKEN_API bool usingNewEnum(); - -} - -namespace Shiboken { namespace Enum { - -enum : int { - ENOPT_OLD_ENUM = 0x00, // no longer supported - ENOPT_NEW_ENUM = 0x01, - ENOPT_INHERIT_INT = 0x02, - ENOPT_GLOBAL_SHORTCUT = 0x04, - ENOPT_SCOPED_SHORTCUT = 0x08, - ENOPT_NO_FAKESHORTCUT = 0x10, - ENOPT_NO_FAKERENAMES = 0x20, - ENOPT_NO_ZERODEFAULT = 0x40, - ENOPT_NO_MISSING = 0x80, -}; - -LIBSHIBOKEN_API extern int enumOption; - -}} - -#endif // SBKENUM_P_H diff --git a/sources/shiboken6/libshiboken/sbkfeature_base.cpp b/sources/shiboken6/libshiboken/sbkfeature_base.cpp index 1242cbda4..fb2fb95aa 100644 --- a/sources/shiboken6/libshiboken/sbkfeature_base.cpp +++ b/sources/shiboken6/libshiboken/sbkfeature_base.cpp @@ -4,7 +4,7 @@ #include "basewrapper.h" #include "basewrapper_p.h" #include "autodecref.h" -#include "sbkenum_p.h" +#include "sbkenum.h" #include "sbkstring.h" #include "sbkstaticstrings.h" #include "sbkstaticstrings_p.h" diff --git a/sources/shiboken6/libshiboken/shiboken.h b/sources/shiboken6/libshiboken/shiboken.h index 13a15e1f4..6c0ecbe30 100644 --- a/sources/shiboken6/libshiboken/shiboken.h +++ b/sources/shiboken6/libshiboken/shiboken.h @@ -14,7 +14,6 @@ #include "sbkarrayconverter.h" #include "sbkconverter.h" #include "sbkenum.h" -#include "sbkenum_p.h" // PYSIDE-1735: This is during the migration, only. #include "sbkerrors.h" #include "sbkmodule.h" #include "sbkstring.h" diff --git a/sources/shiboken6/shibokenmodule/typesystem_shiboken.xml b/sources/shiboken6/shibokenmodule/typesystem_shiboken.xml index 81919d6c3..de5003d5b 100644 --- a/sources/shiboken6/shibokenmodule/typesystem_shiboken.xml +++ b/sources/shiboken6/shibokenmodule/typesystem_shiboken.xml @@ -114,12 +114,6 @@ </inject-code> </add-function> - <add-function signature="_unpickle_enum(PyObject*, PyObject*)" return-type="PyObject*"> - <inject-code> - %PYARG_0 = Shiboken::Enum::unpickleEnum(%1, %2); - </inject-code> - </add-function> - <extra-includes> <include file-name="sbkversion.h" location="local"/> <include file-name="voidptr.h" location="local"/> |
