diff options
Diffstat (limited to 'sources')
20 files changed, 430 insertions, 225 deletions
diff --git a/sources/pyside6/PySide6/QtCore/glue/core_snippets.cpp b/sources/pyside6/PySide6/QtCore/glue/core_snippets.cpp index e58d54998..9b2b40e82 100644 --- a/sources/pyside6/PySide6/QtCore/glue/core_snippets.cpp +++ b/sources/pyside6/PySide6/QtCore/glue/core_snippets.cpp @@ -19,105 +19,6 @@ #include <QtCore/QObject> #include <QtCore/QRegularExpression> #include <QtCore/QStack> -#include <QtCore/QVariant> - -// Helpers for QVariant conversion - -QMetaType QVariant_resolveMetaType(PyTypeObject *type) -{ - if (!PyObject_TypeCheck(type, SbkObjectType_TypeF())) - return {}; - const char *typeName = Shiboken::ObjectType::getOriginalName(type); - if (!typeName) - return {}; - const bool valueType = '*' != typeName[qstrlen(typeName) - 1]; - // Do not convert user type of value - if (valueType && Shiboken::ObjectType::isUserType(type)) - return {}; - QMetaType metaType = QMetaType::fromName(typeName); - if (metaType.isValid()) - return metaType; - // Do not resolve types to value type - if (valueType) - return {}; - // Find in base types. First check tp_bases, and only after check tp_base, because - // tp_base does not always point to the first base class, but rather to the first - // that has added any python fields or slots to its object layout. - // See https://mail.python.org/pipermail/python-list/2009-January/520733.html - if (type->tp_bases) { - const auto size = PyTuple_Size(type->tp_bases); - Py_ssize_t i = 0; - // PYSIDE-1887, PYSIDE-86: Skip QObject base class of QGraphicsObject; - // it needs to use always QGraphicsItem as a QVariant type for - // QGraphicsItem::itemChange() to work. - if (qstrcmp(typeName, "QGraphicsObject*") == 0) - ++i; - for ( ; i < size; ++i) { - auto baseType = reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(type->tp_bases, i)); - const QMetaType derived = QVariant_resolveMetaType(baseType); - if (derived.isValid()) - return derived; - } - } else if (type->tp_base) { - return QVariant_resolveMetaType(type->tp_base); - } - return {}; -} - -QVariant QVariant_convertToValueList(PyObject *list) -{ - if (PySequence_Size(list) < 0) { - // clear the error if < 0 which means no length at all - PyErr_Clear(); - return {}; - } - - Shiboken::AutoDecRef element(PySequence_GetItem(list, 0)); - - auto *type = reinterpret_cast<PyTypeObject *>(element.object()); - QMetaType metaType = QVariant_resolveMetaType(type); - if (!metaType.isValid()) - return {}; - - const QByteArray listTypeName = QByteArrayLiteral("QList<") + metaType.name() + '>'; - metaType = QMetaType::fromName(listTypeName); - if (!metaType.isValid()) - return {}; - - Shiboken::Conversions::SpecificConverter converter(listTypeName); - if (!converter) { - qWarning("Type converter for: %s not registered.", listTypeName.constData()); - return {}; - } - - QVariant var(metaType); - converter.toCpp(list, &var); - return var; -} - -bool QVariant_isStringList(PyObject *list) -{ - if (!PySequence_Check(list)) { - // If it is not a list or a derived list class - // we assume that will not be a String list neither. - return false; - } - - if (PySequence_Size(list) < 0) { - // clear the error if < 0 which means no length at all - PyErr_Clear(); - return false; - } - - Shiboken::AutoDecRef fast(PySequence_Fast(list, "Failed to convert QVariantList")); - const Py_ssize_t size = PySequence_Size(fast.object()); - for (Py_ssize_t i = 0; i < size; ++i) { - Shiboken::AutoDecRef item(PySequence_GetItem(fast.object(), i)); - if (PyUnicode_Check(item) == 0) - return false; - } - return true; -} // Helpers for qAddPostRoutine diff --git a/sources/pyside6/PySide6/QtCore/glue/core_snippets_p.h b/sources/pyside6/PySide6/QtCore/glue/core_snippets_p.h index 11e84b291..4c1867a1a 100644 --- a/sources/pyside6/PySide6/QtCore/glue/core_snippets_p.h +++ b/sources/pyside6/PySide6/QtCore/glue/core_snippets_p.h @@ -14,10 +14,8 @@ QT_FORWARD_DECLARE_CLASS(QGenericArgument) QT_FORWARD_DECLARE_CLASS(QGenericReturnArgument) -QT_FORWARD_DECLARE_CLASS(QMetaType) QT_FORWARD_DECLARE_CLASS(QObject) QT_FORWARD_DECLARE_CLASS(QRegularExpression) -QT_FORWARD_DECLARE_CLASS(QVariant); QT_BEGIN_NAMESPACE namespace QtCoreHelper { @@ -26,14 +24,6 @@ class QGenericReturnArgumentHolder; } QT_END_NAMESPACE -// Helpers for QVariant conversion - -QMetaType QVariant_resolveMetaType(PyTypeObject *type); - -QVariant QVariant_convertToValueList(PyObject *list); - -bool QVariant_isStringList(PyObject *list); - // Helpers for qAddPostRoutine namespace PySide { void globalPostRoutineCallback(); diff --git a/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml b/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml index 207844c56..c3130ccb2 100644 --- a/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml +++ b/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml @@ -16,6 +16,7 @@ <extra-includes> <include file-name="pysidemetatype.h" location="global"/> <include file-name="pysideutils.h" location="global"/> <!-- QString conversion --> + <include file-name="pysidevariantutils.h" location="global"/> <!-- QVariant conversion --> <include file-name="signalmanager.h" location="global"/> <include file-name="sbkerrors.h" location="global"/> <!-- QtCoreHelper::QGenericReturnArgumentHolder --> @@ -320,6 +321,7 @@ <extra-includes> <include file-name="optional" location="global"/> <include file-name="pysideqenum.h" location="global"/> + <include file-name="pysidevariantutils.h" location="global"/> </extra-includes> <conversion-rule> <native-to-target file="../glue/qtcore.cpp" snippet="return-qvariant"/> @@ -391,6 +393,9 @@ </object-type> <primitive-type name="QJsonObject"> + <extra-includes> + <include file-name="pysidevariantutils.h" location="global"/> + </extra-includes> <conversion-rule> <native-to-target file="../glue/qtcore.cpp" snippet="return-qjsonobject"/> <target-to-native> @@ -934,9 +939,12 @@ <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qdatetime-2"/> </add-function> <!-- PYSIDE-1735: Qt::TimeSpec is no more compatible with int --> - <add-function signature="QDateTime(int@year@,int@month@,int@day@,int@h@,int@m@,int@s@,int@ms@,Qt::TimeSpec@spec@=Qt::LocalTime)"> + <add-function signature="QDateTime(int@year@,int@month@,int@day@,int@h@,int@m@,int@s@,int@ms@,Qt::TimeSpec@spec@)"> <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qdatetime-3"/> </add-function> + <add-function signature="QDateTime(int@year@,int@month@,int@day@,int@h@,int@m@,int@s@,int@ms@,QTimeZone@spec@=QTimeZone::LocalTime)" since="6.5"> + <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qdatetime-4"/> + </add-function> <add-function signature="__repr__" return-type="str"> <inject-code class="target" position="beginning"> <insert-template name="repr_code"> diff --git a/sources/pyside6/PySide6/glue/qtcore.cpp b/sources/pyside6/PySide6/glue/qtcore.cpp index 78a25f0a1..cd1462676 100644 --- a/sources/pyside6/PySide6/glue/qtcore.cpp +++ b/sources/pyside6/PySide6/glue/qtcore.cpp @@ -225,49 +225,6 @@ return %out; // @snippet conversion-qmetatype-pytypeobject // @snippet qvariant-conversion -static QVariant QVariant_convertToVariantMap(PyObject *map) -{ - Py_ssize_t pos = 0; - Shiboken::AutoDecRef keys(PyDict_Keys(map)); - if (!QVariant_isStringList(keys)) - return {}; - PyObject *key{}; - PyObject *value{}; - QMap<QString,QVariant> ret; - while (PyDict_Next(map, &pos, &key, &value)) { - QString cppKey = %CONVERTTOCPP[QString](key); - QVariant cppValue = %CONVERTTOCPP[QVariant](value); - ret.insert(cppKey, cppValue); - } - return QVariant(ret); -} -static QVariant QVariant_convertToVariantList(PyObject *list) -{ - if (QVariant_isStringList(list)) { - QList<QString > lst = %CONVERTTOCPP[QList<QString>](list); - return QVariant(QStringList(lst)); - } - QVariant valueList = QVariant_convertToValueList(list); - if (valueList.isValid()) - return valueList; - - if (PySequence_Size(list) < 0) { - // clear the error if < 0 which means no length at all - PyErr_Clear(); - return {}; - } - - QList<QVariant> lst; - Shiboken::AutoDecRef fast(PySequence_Fast(list, "Failed to convert QVariantList")); - const Py_ssize_t size = PySequence_Size(fast.object()); - for (Py_ssize_t i = 0; i < size; ++i) { - Shiboken::AutoDecRef pyItem(PySequence_GetItem(fast.object(), i)); - QVariant item = %CONVERTTOCPP[QVariant](pyItem); - lst.append(item); - } - return QVariant(lst); -} - using SpecificConverter = Shiboken::Conversions::SpecificConverter; static std::optional<SpecificConverter> converterForQtType(const char *typeNameC) @@ -535,6 +492,12 @@ QTime time(%4, %5, %6, %7); Shiboken::Warnings::warnDeprecated("QDateTime", "QDateTime(..., Qt::TimeSpec spec)"); // @snippet qdatetime-3 +// @snippet qdatetime-4 +QDate date(%1, %2, %3); +QTime time(%4, %5, %6, %7); +%0 = new %TYPE(date, time, QTimeZone(%8)); +// @snippet qdatetime-4 + // @snippet qdatetime-topython QDate date = %CPPSELF.date(); QTime time = %CPPSELF.time(); @@ -1538,7 +1501,7 @@ if (Shiboken::Enum::check(%in)) { metaType = QMetaType::fromName(typeName); } if (!metaType.isValid()) - metaType = QVariant_resolveMetaType(Py_TYPE(%in)); + metaType = PySide::Variant::resolveMetaType(Py_TYPE(%in)); bool ok = false; if (metaType.isValid()) { @@ -1560,12 +1523,12 @@ if (!ok) // @snippet conversion-sbkobject // @snippet conversion-pydict -QVariant ret = QVariant_convertToVariantMap(%in); +QVariant ret = PySide::Variant::convertToVariantMap(%in); %out = ret.isValid() ? ret : QVariant::fromValue(PySide::PyObjectWrapper(%in)); // @snippet conversion-pydict // @snippet conversion-pylist -QVariant ret = QVariant_convertToVariantList(%in); +QVariant ret = PySide::Variant::convertToVariantList(%in); %out = ret.isValid() ? ret : QVariant::fromValue(PySide::PyObjectWrapper(%in)); // @snippet conversion-pylist @@ -1575,7 +1538,7 @@ QVariant ret = QVariant_convertToVariantList(%in); // @snippet conversion-pyobject // @snippet conversion-qjsonobject-pydict -QVariant dict = QVariant_convertToVariantMap(%in); +QVariant dict = PySide::Variant::convertToVariantMap(%in); QJsonValue val = QJsonValue::fromVariant(dict); %out = val.toObject(); // @snippet conversion-qjsonobject-pydict diff --git a/sources/pyside6/libpyside/CMakeLists.txt b/sources/pyside6/libpyside/CMakeLists.txt index 539f1f329..15ab47494 100644 --- a/sources/pyside6/libpyside/CMakeLists.txt +++ b/sources/pyside6/libpyside/CMakeLists.txt @@ -40,6 +40,7 @@ set(libpyside_HEADERS # installed below pysideslot_p.h pysidestaticstrings.h pysideutils.h + pysidevariantutils.h pysideweakref.h qobjectconnect.h signalmanager.h @@ -59,6 +60,7 @@ set(libpyside_SRC pysidesignal.cpp pysideslot.cpp pysideproperty.cpp + pysidevariantutils.cpp pysideweakref.cpp pyside.cpp pyside_numpy.cpp diff --git a/sources/pyside6/libpyside/pysidevariantutils.cpp b/sources/pyside6/libpyside/pysidevariantutils.cpp new file mode 100644 index 000000000..7dbfb3afc --- /dev/null +++ b/sources/pyside6/libpyside/pysidevariantutils.cpp @@ -0,0 +1,212 @@ +// Copyright (C) 2025 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 "pysidevariantutils.h" +#include "pysideutils.h" + +#include <QtCore/qvariantmap.h> + +#include <autodecref.h> +#include <sbkconverter.h> +#include <basewrapper.h> + +using namespace Qt::StringLiterals; + +static const char qVariantTypeName[] = "QVariant"; + +static void warnConverter(const char *name) +{ + qWarning("Type converter for: %s not registered.", name); +} + +// Helper converting each item of a non-empty list using the "QVariant" converter +static std::optional<QVariantList> pyListToVariantListHelper(PyObject *list, Py_ssize_t size) +{ + Q_ASSERT(size > 0); + QVariantList result; + result.reserve(size); + Shiboken::Conversions::SpecificConverter converter(qVariantTypeName); + if (!converter) { + warnConverter(qVariantTypeName); + return std::nullopt; + } + for (Py_ssize_t i = 0; i < size; ++i) { + Shiboken::AutoDecRef pyItem(PySequence_GetItem(list, i)); + QVariant item; + converter.toCpp(pyItem.object(), &item); + result.append(item); + } + return result; +} + +// Helper checking for a sequence of Unicode objects +static bool isStringList(PyObject *list) +{ + const Py_ssize_t size = PySequence_Size(list); + if (size == 0) + return false; + for (Py_ssize_t i = 0; i < size; ++i) { + Shiboken::AutoDecRef item(PySequence_GetItem(list, i)); + if (PyUnicode_Check(item) == 0) + return false; + } + return true; +} + +// Helper to convert to a QStringList +static std::optional<QStringList> listToStringList(PyObject *list) +{ + static const char listType[] = "QList<QString>"; + Shiboken::Conversions::SpecificConverter converter(listType); + if (!converter) { + warnConverter(listType); + return std::nullopt; + } + QStringList result; + converter.toCpp(list, &result); + return result; +} + +// Helper to convert a non-empty, homogenous list using the converter of the first item +static QVariant convertToValueList(PyObject *list) +{ + Q_ASSERT(PySequence_Size(list) >= 0); + + Shiboken::AutoDecRef element(PySequence_GetItem(list, 0)); + + auto *type = reinterpret_cast<PyTypeObject *>(element.object()); + QMetaType metaType = PySide::Variant::resolveMetaType(type); + if (!metaType.isValid()) + return {}; + + const QByteArray listTypeName = QByteArrayLiteral("QList<") + metaType.name() + '>'; + metaType = QMetaType::fromName(listTypeName); + if (!metaType.isValid()) + return {}; + + Shiboken::Conversions::SpecificConverter converter(listTypeName); + if (!converter) { + warnConverter(listTypeName.constData()); + return {}; + } + + QVariant var(metaType); + converter.toCpp(list, &var); + return var; +} + +namespace PySide::Variant +{ + +QMetaType resolveMetaType(PyTypeObject *type) +{ + if (!PyObject_TypeCheck(type, SbkObjectType_TypeF())) + return {}; + const char *typeName = Shiboken::ObjectType::getOriginalName(type); + if (!typeName) + return {}; + const bool valueType = '*' != typeName[qstrlen(typeName) - 1]; + // Do not convert user type of value + if (valueType && Shiboken::ObjectType::isUserType(type)) + return {}; + QMetaType metaType = QMetaType::fromName(typeName); + if (metaType.isValid()) + return metaType; + // Do not resolve types to value type + if (valueType) + return {}; + // Find in base types. First check tp_bases, and only after check tp_base, because + // tp_base does not always point to the first base class, but rather to the first + // that has added any python fields or slots to its object layout. + // See https://mail.python.org/pipermail/python-list/2009-January/520733.html + if (type->tp_bases) { + const auto size = PyTuple_Size(type->tp_bases); + Py_ssize_t i = 0; + // PYSIDE-1887, PYSIDE-86: Skip QObject base class of QGraphicsObject; + // it needs to use always QGraphicsItem as a QVariant type for + // QGraphicsItem::itemChange() to work. + if (qstrcmp(typeName, "QGraphicsObject*") == 0) + ++i; + for ( ; i < size; ++i) { + auto baseType = reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(type->tp_bases, i)); + const QMetaType derived = resolveMetaType(baseType); + if (derived.isValid()) + return derived; + } + return {}; + } + if (type->tp_base != nullptr) + return resolveMetaType(type->tp_base); + return {}; +} + +std::optional<QVariantList> pyListToVariantList(PyObject *list) +{ + if (list == nullptr || PySequence_Check(list) == 0) + return std::nullopt; + const auto size = PySequence_Size(list); + if (size < 0) { // Some infinite (I/O read) thing? - bail out + PyErr_Clear(); + return std::nullopt; + } + if (size == 0) + return QVariantList{}; + return pyListToVariantListHelper(list, size); +} + +QVariant convertToVariantList(PyObject *list) +{ + const auto size = PySequence_Size(list); + if (size < 0) { // Some infinite (I/O read) thing? - bail out + PyErr_Clear(); + return {}; + } + if (size == 0) + return QVariantList{}; + + if (isStringList(list)) { + auto stringListO = listToStringList(list); + if (stringListO.has_value()) + return {stringListO.value()}; + } + + if (QVariant valueList = convertToValueList(list); valueList.isValid()) + return valueList; + + if (auto vlO = pyListToVariantListHelper(list, size); vlO.has_value()) + return vlO.value(); + + return {}; +} + +QVariant convertToVariantMap(PyObject *map) +{ + if (map == nullptr || PyDict_Check(map) == 0) + return {}; + + QVariantMap result; + if (PyDict_Size(map) == 0) + return result; + + Py_ssize_t pos = 0; + Shiboken::AutoDecRef keys(PyDict_Keys(map)); + if (!isStringList(keys)) + return {}; + + Shiboken::Conversions::SpecificConverter converter(qVariantTypeName); + if (!converter) { + warnConverter(qVariantTypeName); + return {}; + } + + PyObject *key{}; + PyObject *value{}; + while (PyDict_Next(map, &pos, &key, &value)) { + QVariant cppValue; + converter.toCpp(value, &cppValue); + result.insert(PySide::pyUnicodeToQString(key), cppValue); + } + return result; +} + +} // namespace PySide::Variant diff --git a/sources/pyside6/libpyside/pysidevariantutils.h b/sources/pyside6/libpyside/pysidevariantutils.h new file mode 100644 index 000000000..b53f7ce82 --- /dev/null +++ b/sources/pyside6/libpyside/pysidevariantutils.h @@ -0,0 +1,37 @@ +// Copyright (C) 2025 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 PYSIDEVARIANTUTILS_H +#define PYSIDEVARIANTUTILS_H + +#include <sbkpython.h> + +#include <pysidemacros.h> + +#include <QtCore/qvariant.h> +#include <QtCore/qvariantlist.h> + +#include <optional> + +namespace PySide::Variant +{ + +/// Return a QMetaType for a PyTypeObject for purposes of +/// converting to a QVariant. +PYSIDE_API QMetaType resolveMetaType(PyTypeObject *type); + +/// Convert a heterogenous Python list to a QVariantList by converting each +/// item using the QVariant converter. +PYSIDE_API std::optional<QVariantList> pyListToVariantList(PyObject *list); + +/// Converts a list to a QVariant following the PySide semantics: +/// - A list of strings is returned as QVariant<QStringList> +/// - A list of convertible values is returned as QVariant<QList<Value>> +/// - Remaining types are returned as QVariant(QVariantList) +PYSIDE_API QVariant convertToVariantList(PyObject *list); + +/// Converts a map to a QVariantMap (string keys and QVariant values) +PYSIDE_API QVariant convertToVariantMap(PyObject *map); +} // namespace PySide::Variant + +#endif // PYSIDEVARIANTUTILS_H diff --git a/sources/pyside6/tests/QtCore/deepcopy_test.py b/sources/pyside6/tests/QtCore/deepcopy_test.py index 8b211a979..64b001e6d 100644 --- a/sources/pyside6/tests/QtCore/deepcopy_test.py +++ b/sources/pyside6/tests/QtCore/deepcopy_test.py @@ -12,8 +12,8 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) from init_paths import init_test_paths init_test_paths(False) -from PySide6.QtCore import QByteArray, QDate, QDateTime, QTime, QLine, QLineF -from PySide6.QtCore import Qt, QSize, QSizeF, QRect, QRectF, QDir, QPoint, QPointF +from PySide6.QtCore import QByteArray, QDate, QDateTime, QTime, QLine, QLineF, QTimeZone +from PySide6.QtCore import QSize, QSizeF, QRect, QRectF, QDir, QPoint, QPointF try: from PySide6.QtCore import QUuid HAVE_Q = True @@ -45,7 +45,7 @@ class QTimeDeepCopy(DeepCopyHelper, unittest.TestCase): class QDateTimeDeepCopy(DeepCopyHelper, unittest.TestCase): def setUp(self): - self.original = QDateTime(2010, 5, 18, 10, 24, 45, 223, Qt.TimeSpec.LocalTime) + self.original = QDateTime(2010, 5, 18, 10, 24, 45, 223, QTimeZone(QTimeZone.LocalTime)) class QSizeDeepCopy(DeepCopyHelper, unittest.TestCase): diff --git a/sources/pyside6/tests/QtCore/repr_test.py b/sources/pyside6/tests/QtCore/repr_test.py index 084b69f87..52a426d31 100644 --- a/sources/pyside6/tests/QtCore/repr_test.py +++ b/sources/pyside6/tests/QtCore/repr_test.py @@ -13,8 +13,8 @@ init_test_paths(False) # for 'self.original' import PySide6 # noqa -from PySide6.QtCore import QByteArray, QDate, QDateTime, QTime, QLine, QLineF -from PySide6.QtCore import Qt, QSize, QSizeF, QRect, QRectF, QPoint, QPointF +from PySide6.QtCore import QByteArray, QDate, QDateTime, QTime, QLine, QLineF, QTimeZone +from PySide6.QtCore import QSize, QSizeF, QRect, QRectF, QPoint, QPointF try: from PySide6.QtCore import QUuid HAVE_Q = True @@ -46,7 +46,7 @@ class QTimeReprCopy(ReprCopyHelper, unittest.TestCase): class QDateTimeReprCopy(ReprCopyHelper, unittest.TestCase): def setUp(self): - self.original = QDateTime(2010, 5, 18, 10, 24, 45, 223, Qt.TimeSpec.LocalTime) + self.original = QDateTime(2010, 5, 18, 10, 24, 45, 223, QTimeZone(QTimeZone.LocalTime)) class QSizeReprCopy(ReprCopyHelper, unittest.TestCase): diff --git a/sources/shiboken6/ApiExtractor/apiextractor.cpp b/sources/shiboken6/ApiExtractor/apiextractor.cpp index 90f4f5dd9..38dedfd14 100644 --- a/sources/shiboken6/ApiExtractor/apiextractor.cpp +++ b/sources/shiboken6/ApiExtractor/apiextractor.cpp @@ -370,6 +370,7 @@ bool ApiExtractorPrivate::runHelper(ApiExtractorFlags flags) bool addCompilerSupportArguments = true; if (clangOptionsSize > 0) { + clang::setTargetTriple(m_clangOptions); qsizetype i = 0; if (m_clangOptions.at(i) == u"-") { ++i; diff --git a/sources/shiboken6/ApiExtractor/clangparser/clangparser.cpp b/sources/shiboken6/ApiExtractor/clangparser/clangparser.cpp index 54a1a2c8b..747937ede 100644 --- a/sources/shiboken6/ApiExtractor/clangparser/clangparser.cpp +++ b/sources/shiboken6/ApiExtractor/clangparser/clangparser.cpp @@ -275,6 +275,24 @@ static CXTranslationUnit createTranslationUnit(CXIndex index, return tu; } +static void setupTarget(CXTranslationUnit translationUnit) +{ + const CXTargetInfo targetInfo = clang_getTranslationUnitTargetInfo(translationUnit); + const auto tripleCS = clang_TargetInfo_getTriple(targetInfo); + clang::setPointerSize(clang_TargetInfo_getPointerWidth(targetInfo)); + clang::setTargetTriple(QString::fromUtf8(clang_getCString(tripleCS))); + clang_disposeString(tripleCS); + + QString message; + { + QTextStream str(&message); + str << "CLANG v" << CINDEX_VERSION_MAJOR << '.' << CINDEX_VERSION_MINOR + << " targeting \"" << targetTriple() << "\", " << pointerSize() << "bit."; + } + qCInfo(lcShiboken, "%s", qPrintable(message)); + ReportHandler::addGeneralMessage(message + u'\n'); +} + /* clangFlags are flags to clang_parseTranslationUnit2() such as * CXTranslationUnit_KeepGoing (from CINDEX_VERSION_MAJOR/CINDEX_VERSION_MINOR 0.35) */ @@ -295,6 +313,8 @@ bool parse(const QByteArrayList &clangArgs, bool addCompilerSupportArguments, if (!translationUnit) return false; + setupTarget(translationUnit); + CXCursor rootCursor = clang_getTranslationUnitCursor(translationUnit); clang_visitChildren(rootCursor, visitorCallback, reinterpret_cast<CXClientData>(&bv)); diff --git a/sources/shiboken6/ApiExtractor/clangparser/compilersupport.cpp b/sources/shiboken6/ApiExtractor/clangparser/compilersupport.cpp index ed8057b62..4d93a084f 100644 --- a/sources/shiboken6/ApiExtractor/clangparser/compilersupport.cpp +++ b/sources/shiboken6/ApiExtractor/clangparser/compilersupport.cpp @@ -59,6 +59,9 @@ bool setCompiler(const QString &name) QString _compilerPath; // Pre-defined compiler path (from command line) +static unsigned _pointerSize = QT_POINTER_SIZE * 8; +static QString _targetTriple; + const QString &compilerPath() { return _compilerPath; @@ -153,14 +156,15 @@ static void filterHomebrewHeaderPaths(HeaderPaths &headerPaths) if (homebrewPrefix.isEmpty()) return; - qCInfo(lcShiboken) << "Found HOMEBREW_OPT with value:" << homebrewPrefix - << "Assuming homebrew build environment."; + ReportHandler::addGeneralMessage("Found HOMEBREW_OPT with value:"_L1 + + QString::fromUtf8(homebrewPrefix) + + "\nAssuming homebrew build environment."_L1); HeaderPaths::iterator it = headerPaths.begin(); while (it != headerPaths.end()) { if (it->path.startsWith(homebrewPrefix)) { - qCInfo(lcShiboken) << "Filtering out homebrew include path: " - << it->path; + ReportHandler::addGeneralMessage("Filtering out homebrew include path: "_L1 + + QString::fromUtf8(it->path)); it = headerPaths.erase(it); } else { ++it; @@ -186,12 +190,6 @@ static HeaderPaths gppInternalIncludePaths(const QString &compiler) const QByteArrayList stdErrLines = stdErr.split('\n'); bool isIncludeDir = false; - if (ReportHandler::isDebug(ReportHandler::MediumDebug)) - qCInfo(lcShiboken()).noquote().nospace() - << "gppInternalIncludePaths:\n compiler: " << compiler - << "\n stdOut: " << stdOut - << "\n stdErr: " << stdErr; - for (const QByteArray &line : stdErrLines) { if (isIncludeDir) { if (line.startsWith(QByteArrayLiteral("End of search list"))) { @@ -212,6 +210,17 @@ static HeaderPaths gppInternalIncludePaths(const QString &compiler) if (platform() == Platform::macOS) filterHomebrewHeaderPaths(result); + QString message; + { + QTextStream str(&message); + str << "gppInternalIncludePaths:\n compiler: " << compiler << '\n'; + for (const auto &h : result) + str << " " << h.path << '\n'; + if (ReportHandler::isDebug(ReportHandler::MediumDebug)) + str << " stdOut: " << stdOut << "\n stdErr: " << stdErr; + } + ReportHandler::addGeneralMessage(message); + return result; } @@ -365,11 +374,10 @@ static void appendClangBuiltinIncludes(HeaderPaths *p) "(neither by checking the environment variables LLVM_INSTALL_DIR, CLANG_INSTALL_DIR " " nor running llvm-config). This may lead to parse errors."); } else { - qCInfo(lcShiboken, "CLANG v%d.%d, builtins includes directory: %s", - CINDEX_VERSION_MAJOR, CINDEX_VERSION_MINOR, - qPrintable(clangBuiltinIncludesDir)); p->append(HeaderPath{QFile::encodeName(clangBuiltinIncludesDir), HeaderType::System}); + ReportHandler::addGeneralMessage("CLANG builtins includes directory: "_L1 + + clangBuiltinIncludesDir); } } @@ -451,4 +459,33 @@ LanguageLevel languageLevelFromOption(const char *o) return LanguageLevel::Default; } +unsigned pointerSize() +{ + return _pointerSize; +} + +void setPointerSize(unsigned ps) +{ + _pointerSize = ps; +} + +QString targetTriple() +{ + return _targetTriple; + +} +void setTargetTriple(const QString &t) +{ + _targetTriple = t; +} + +void setTargetTriple(const QStringList &clangOptions) +{ + static constexpr auto targetOption = "--target="_L1; + auto targetOptionPred = [](const QString &o) { return o.startsWith(targetOption); }; + const auto it = std::find_if(clangOptions.cbegin(), clangOptions.cend(), targetOptionPred); + if (it != clangOptions.cend()) + _targetTriple = it->sliced(targetOption.size()); +} + } // namespace clang diff --git a/sources/shiboken6/ApiExtractor/clangparser/compilersupport.h b/sources/shiboken6/ApiExtractor/clangparser/compilersupport.h index 1fa980998..0e12ca137 100644 --- a/sources/shiboken6/ApiExtractor/clangparser/compilersupport.h +++ b/sources/shiboken6/ApiExtractor/clangparser/compilersupport.h @@ -51,6 +51,14 @@ void setCompilerPath(const QString &name); Platform platform(); bool setPlatform(const QString &name); + +unsigned pointerSize(); // (bit) +void setPointerSize(unsigned ps); // Set by parser + +QString targetTriple(); +void setTargetTriple(const QStringList &clangOptions); // Set from cmd line before parsing +void setTargetTriple(const QString &t); // Updated by clang parser while parsing + } // namespace clang #endif // COMPILERSUPPORT_H diff --git a/sources/shiboken6/generator/shiboken/cppgenerator.cpp b/sources/shiboken6/generator/shiboken/cppgenerator.cpp index 21e167b53..539094075 100644 --- a/sources/shiboken6/generator/shiboken/cppgenerator.cpp +++ b/sources/shiboken6/generator/shiboken/cppgenerator.cpp @@ -997,7 +997,7 @@ void CppGenerator::writeCacheResetNative(TextStream &s, const GeneratorContext & { s << "void " << classContext.wrapperName() << "::resetPyMethodCache()\n{\n" << indent - << "std::fill_n(m_PyMethodCache, sizeof(m_PyMethodCache) / sizeof(m_PyMethodCache[0]), false);\n" + << "std::fill(m_PyMethodCache.begin(), m_PyMethodCache.end(), nullptr);\n" << outdent << "}\n\n"; } diff --git a/sources/shiboken6/generator/shiboken/headergenerator.cpp b/sources/shiboken6/generator/shiboken/headergenerator.cpp index 965d80dad..7616fa5c3 100644 --- a/sources/shiboken6/generator/shiboken/headergenerator.cpp +++ b/sources/shiboken6/generator/shiboken/headergenerator.cpp @@ -185,6 +185,7 @@ void HeaderGenerator::writeWrapperClass(TextStream &s, for( const auto &includeGroup : includeGroups) s << includeGroup; } + s << "#include <sbkpython.h>\n\n#include <array>\n"; s << "namespace Shiboken { class AutoDecRef; class GilState; }\n\n"; @@ -288,9 +289,10 @@ void *qt_metacast(const char *_clname) override; } if (needsMethodCache) { - s << "mutable bool m_PyMethodCache[" << maxOverrides << "] = {false"; + s << "mutable std::array<PyObject *, " << maxOverrides + << "> m_PyMethodCache = {nullptr"; for (int i = 1; i < maxOverrides; ++i) - s << ", false"; + s << ", nullptr"; s << "};\n"; } diff --git a/sources/shiboken6/libshiboken/basewrapper.cpp b/sources/shiboken6/libshiboken/basewrapper.cpp index 3e26b4605..039cd3085 100644 --- a/sources/shiboken6/libshiboken/basewrapper.cpp +++ b/sources/shiboken6/libshiboken/basewrapper.cpp @@ -21,6 +21,7 @@ #include "voidptr.h" #include <algorithm> +#include <cctype> #include <cstddef> #include <cstring> #include <iostream> @@ -786,32 +787,57 @@ static PyObject *overrideMethodName(PyObject *pySelf, const char *methodName, // The virtual function call PyObject *Sbk_GetPyOverride(const void *voidThis, PyTypeObject *typeObject, Shiboken::GilState &gil, const char *funcName, - bool &resultCache, PyObject **nameCache) -{ - PyObject *pyOverride{}; - if (!resultCache) { - gil.acquire(); - auto &bindingManager = Shiboken::BindingManager::instance(); - SbkObject *wrapper = bindingManager.retrieveWrapper(voidThis, typeObject); - // The refcount can be 0 if the object is dieing and someone called - // a virtual method from the destructor - if (wrapper == nullptr) - return nullptr; - auto *pySelf = reinterpret_cast<PyObject *>(wrapper); - if (Py_REFCNT(pySelf) == 0) - return nullptr; - PyObject *pyMethodName = overrideMethodName(pySelf, funcName, nameCache); - pyOverride = Shiboken::BindingManager::getOverride(wrapper, pyMethodName); - if (pyOverride == nullptr) { - resultCache = true; - gil.release(); - } else if (Shiboken::Errors::occurred() != nullptr) { - // Give up. - Py_XDECREF(pyOverride); - pyOverride = nullptr; - } + PyObject *&resultCache, PyObject **nameCache) +{ + if (Py_IsInitialized() == 0 || resultCache == Py_None) + return nullptr; // Bail out, execute C++ call (wrappers may outlive Python). + + auto &bindingManager = Shiboken::BindingManager::instance(); + SbkObject *wrapper = bindingManager.retrieveWrapper(voidThis, typeObject); + // The refcount can be 0 if the object is dieing and someone called + // a virtual method from the destructor + if (wrapper == nullptr) + return nullptr; + auto *pySelf = reinterpret_cast<PyObject *>(wrapper); + if (Py_REFCNT(pySelf) == 0) + return nullptr; + + gil.acquire(); + + if (resultCache != nullptr) // recreate the callable from function/self + return PepExt_Type_CallDescrGet(resultCache, pySelf, nullptr); + + PyObject *pyMethodName = overrideMethodName(pySelf, funcName, nameCache); + auto *wrapper_dict = SbkObject_GetDict_NoRef(pySelf); + + // Note: This special case was implemented for duck-punching, which happens + // in the instance dict. It does not work with properties. + // This is not cached to avoid leaking. FIXME PYSIDE 7: Remove (PYSIDE-2916)? + if (PyObject *method = PyDict_GetItem(wrapper_dict, pyMethodName)) { + Py_INCREF(method); + return method; + } + + auto *pyOverride = Shiboken::BindingManager::getOverride(wrapper, pyMethodName); + if (pyOverride == nullptr) { + resultCache = Py_None; + Py_INCREF(resultCache); + gil.release(); + return nullptr; // No override, execute C++ call } - return pyOverride; + + if (Shiboken::Errors::occurred() != nullptr) { + // Give up. + Py_XDECREF(pyOverride); + resultCache = Py_None; + Py_INCREF(resultCache); + gil.release(); + return nullptr; // // Give up. + } + + resultCache = pyOverride; + // recreate the callable from function/self + return PepExt_Type_CallDescrGet(resultCache, pySelf, nullptr); } namespace diff --git a/sources/shiboken6/libshiboken/basewrapper.h b/sources/shiboken6/libshiboken/basewrapper.h index 9eea89540..426298bcf 100644 --- a/sources/shiboken6/libshiboken/basewrapper.h +++ b/sources/shiboken6/libshiboken/basewrapper.h @@ -123,9 +123,8 @@ LIBSHIBOKEN_API PyObject *Sbk_ReturnFromPython_Self(PyObject *self); } // extern "C" LIBSHIBOKEN_API PyObject *Sbk_GetPyOverride(const void *voidThis, PyTypeObject *typeObject, - Shiboken::GilState &gil, - const char *funcName, bool &resultCache, - PyObject **nameCache); + Shiboken::GilState &gil, const char *funcName, + PyObject *&resultCache, PyObject **nameCache); namespace Shiboken { diff --git a/sources/shiboken6/libshiboken/bindingmanager.cpp b/sources/shiboken6/libshiboken/bindingmanager.cpp index 25cc5c00a..ca509aefd 100644 --- a/sources/shiboken6/libshiboken/bindingmanager.cpp +++ b/sources/shiboken6/libshiboken/bindingmanager.cpp @@ -369,13 +369,6 @@ SbkObject *BindingManager::retrieveWrapper(const void *cptr, PyTypeObject *typeO PyObject *BindingManager::getOverride(SbkObject *wrapper, PyObject *pyMethodName) { auto *obWrapper = reinterpret_cast<PyObject *>(wrapper); - auto *wrapper_dict = SbkObject_GetDict_NoRef(obWrapper); - if (PyObject *method = PyDict_GetItem(wrapper_dict, pyMethodName)) { - // Note: This special case was implemented for duck-punching, which happens - // in the instance dict. It does not work with properties. - Py_INCREF(method); - return method; - } Shiboken::AutoDecRef method(PyObject_GetAttr(obWrapper, pyMethodName)); if (method.isNull()) @@ -387,9 +380,9 @@ PyObject *BindingManager::getOverride(SbkObject *wrapper, PyObject *pyMethodName // crude check for them. // PYSIDE-535: This macro is redefined in a compatible way in pep384 if (PyMethod_Check(method) != 0) { - if (PyMethod_GET_SELF(method) != obWrapper) + if (PyMethod_Self(method) != obWrapper) return nullptr; - function = PyMethod_GET_FUNCTION(method); + function = PyMethod_Function(method); } else if (isCompiledMethod(method)) { Shiboken::AutoDecRef im_self(PyObject_GetAttr(method, PyName::im_self())); // Not retaining a reference inline with what PyMethod_GET_SELF does. @@ -413,13 +406,13 @@ PyObject *BindingManager::getOverride(SbkObject *wrapper, PyObject *pyMethodName if (PyObject *defaultMethod = PyDict_GetItem(parentDict.object(), pyMethodName)) { defaultFound = true; if (function != defaultMethod) - return method.release(); + return function; } } } // PYSIDE-2255: If no default method was found, use the method. if (!defaultFound) - return method.release(); + return function; return nullptr; } diff --git a/sources/shiboken6/tests/libsample/bucket.cpp b/sources/shiboken6/tests/libsample/bucket.cpp index cafd382a9..43a32f414 100644 --- a/sources/shiboken6/tests/libsample/bucket.cpp +++ b/sources/shiboken6/tests/libsample/bucket.cpp @@ -40,15 +40,21 @@ bool Bucket::empty() void Bucket::lock() { - m_locked = true; - while (m_locked) { - SLEEP(300); + bool expected = false; + if (m_locked.compare_exchange_strong(expected, true)) { + while (m_locked) { + SLEEP(300); + } + } else { + std::cerr << __FUNCTION__ << " Attempt to lock twice.\n"; } } void Bucket::unlock() { - m_locked = false; + bool expected = true; + if (!m_locked.compare_exchange_strong(expected, false)) + std::cerr << __FUNCTION__ << " Attempt to unlock twice.\n"; } bool Bucket::virtualBlockerMethod() diff --git a/sources/shiboken6/tests/libsample/bucket.h b/sources/shiboken6/tests/libsample/bucket.h index 73e8edd78..677069fe7 100644 --- a/sources/shiboken6/tests/libsample/bucket.h +++ b/sources/shiboken6/tests/libsample/bucket.h @@ -7,6 +7,7 @@ #include "libsamplemacros.h" #include "objecttype.h" +#include <atomic> #include <list> class ObjectType; @@ -19,7 +20,7 @@ public: int pop(); bool empty(); void lock(); - inline bool locked() { return m_locked; } + bool locked() { return m_locked.load(); } void unlock(); virtual bool virtualBlockerMethod(); @@ -27,8 +28,7 @@ public: private: std::list<int> m_data; - - volatile bool m_locked = false; + std::atomic<bool> m_locked{false}; }; #endif // BUCKET_H |