diff options
author | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2017-08-22 13:36:42 +0200 |
---|---|---|
committer | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2017-08-22 13:36:45 +0200 |
commit | 5495989aa1b0161512cc1f73cb0e3a3deb1cf4d1 (patch) | |
tree | 4a6d78b6927cb636f0b34650e4d8508f3729c762 | |
parent | 2c6b753b9e2d262477c7171116d5f97d26698001 (diff) | |
parent | ecef77d751369cf7d66d5fccd110a30237755cdc (diff) |
Merge remote-tracking branch 'origin/5.9' into dev
Change-Id: If8f06cfc86819c193647e5a093a1aaa63942b838
40 files changed, 1439 insertions, 69 deletions
diff --git a/sources/pyside2-tools b/sources/pyside2-tools -Subproject f68388cf547c0d63a5d4a145f65aa9fa90529d5 +Subproject 413ecc73fbe6d6717ae2132e86648ac8b6da9d3 diff --git a/sources/pyside2/CMakeLists.txt b/sources/pyside2/CMakeLists.txt index d5a12379b..d517d0ad7 100644 --- a/sources/pyside2/CMakeLists.txt +++ b/sources/pyside2/CMakeLists.txt @@ -171,6 +171,52 @@ else() CACHE STRING "PySide version [full]" FORCE) endif() +string(TIMESTAMP PYSIDE_BUILD_DATE "%Y-%m-%dT%H:%M:%S+00:00" UTC) +if (PYSIDE_BUILD_DATE) + set(PYSIDE_BUILD_DATE "__build_date__ = '${PYSIDE_BUILD_DATE}'") +endif() + +find_package(Git) +if(GIT_FOUND) + # Check if current source folder is inside a git repo, so that commit information can be + # queried. + execute_process( + COMMAND ${GIT_EXECUTABLE} rev-parse --git-dir + OUTPUT_VARIABLE PYSIDE_SOURCE_IS_INSIDE_REPO + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + + if(PYSIDE_SOURCE_IS_INSIDE_REPO) + # Force git dates to be UTC-based. + set(ENV{TZ} UTC) + execute_process( + COMMAND ${GIT_EXECUTABLE} --no-pager show --date=format-local:%Y-%m-%dT%H:%M:%S+00:00 -s --format=%cd HEAD + OUTPUT_VARIABLE PYSIDE_BUILD_COMMIT_DATE + OUTPUT_STRIP_TRAILING_WHITESPACE) + if(PYSIDE_BUILD_COMMIT_DATE) + set(PYSIDE_BUILD_COMMIT_DATE "__build_commit_date__ = '${PYSIDE_BUILD_COMMIT_DATE}'") + endif() + unset(ENV{TZ}) + + execute_process( + COMMAND ${GIT_EXECUTABLE} rev-parse HEAD + OUTPUT_VARIABLE PYSIDE_BUILD_COMMIT_HASH + OUTPUT_STRIP_TRAILING_WHITESPACE) + if(PYSIDE_BUILD_COMMIT_HASH) + set(PYSIDE_BUILD_COMMIT_HASH "__build_commit_hash__ = '${PYSIDE_BUILD_COMMIT_HASH}'") + endif() + + execute_process( + COMMAND ${GIT_EXECUTABLE} describe HEAD + OUTPUT_VARIABLE PYSIDE_BUILD_COMMIT_HASH_DESCRIBED + OUTPUT_STRIP_TRAILING_WHITESPACE) + if(PYSIDE_BUILD_COMMIT_HASH_DESCRIBED) + set(PYSIDE_BUILD_COMMIT_HASH_DESCRIBED "__build_commit_hash_described__ = '${PYSIDE_BUILD_COMMIT_HASH_DESCRIBED}'") + endif() + + endif() +endif() + include(PySideModules) macro(COLLECT_MODULE_IF_FOUND shortname) diff --git a/sources/pyside2/PySide2/QtCore/CMakeLists.txt b/sources/pyside2/PySide2/QtCore/CMakeLists.txt index afef603c7..0439f0a5c 100644 --- a/sources/pyside2/PySide2/QtCore/CMakeLists.txt +++ b/sources/pyside2/PySide2/QtCore/CMakeLists.txt @@ -136,6 +136,8 @@ ${QtCore_GEN_DIR}/qtime_wrapper.cpp ${QtCore_GEN_DIR}/qtimeline_wrapper.cpp ${QtCore_GEN_DIR}/qtimer_wrapper.cpp ${QtCore_GEN_DIR}/qtimerevent_wrapper.cpp +${QtCore_GEN_DIR}/qtimezone_wrapper.cpp +${QtCore_GEN_DIR}/qtimezone_offsetdata_wrapper.cpp ${QtCore_GEN_DIR}/qtranslator_wrapper.cpp ${QtCore_GEN_DIR}/qurl_wrapper.cpp ${QtCore_GEN_DIR}/qurlquery_wrapper.cpp diff --git a/sources/pyside2/PySide2/QtCore/typesystem_core_common.xml b/sources/pyside2/PySide2/QtCore/typesystem_core_common.xml index b8e070788..efcc5d955 100644 --- a/sources/pyside2/PySide2/QtCore/typesystem_core_common.xml +++ b/sources/pyside2/PySide2/QtCore/typesystem_core_common.xml @@ -1787,6 +1787,12 @@ </modify-function> </value-type> + <value-type name="QTimeZone"> + <enum-type name="TimeType"/> + <enum-type name="NameType"/> + <value-type name="OffsetData"/> + </value-type> + <!-- FIXME QT5: Remove QUuid because cyclic dependency found on overloaddata QUuid(), this lead to cyclic dependency in <<(QDataStream &, QUUid) and incorrect QDataStream code generation (Windows only)--> <!-- <value-type name="QUuid"> diff --git a/sources/pyside2/PySide2/QtGui/typesystem_gui_common.xml b/sources/pyside2/PySide2/QtGui/typesystem_gui_common.xml index 0d9b56edd..343b76229 100644 --- a/sources/pyside2/PySide2/QtGui/typesystem_gui_common.xml +++ b/sources/pyside2/PySide2/QtGui/typesystem_gui_common.xml @@ -639,7 +639,7 @@ %0 = new %TYPE(QPixmap::fromImage(%1)); </inject-code> </add-function> - <modify-function signature="QPixmap(const char **)"> + <modify-function signature="QPixmap(const char*[])"> <modify-argument index="1"> <replace-type modified-type="PySequence" /> </modify-argument> @@ -868,7 +868,7 @@ <modify-function signature="QImage(const uchar*,int,int,QImage::Format,QImageCleanupFunction, void *)" remove="all" /> <!-- ### --> - <modify-function signature="QImage(const char**)"> + <modify-function signature="QImage(const char*[])"> <modify-argument index="1"> <replace-type modified-type="PySequence" /> </modify-argument> diff --git a/sources/pyside2/PySide2/QtMultimedia/typesystem_multimedia_common.xml b/sources/pyside2/PySide2/QtMultimedia/typesystem_multimedia_common.xml index 9565b4334..f708d46ab 100644 --- a/sources/pyside2/PySide2/QtMultimedia/typesystem_multimedia_common.xml +++ b/sources/pyside2/PySide2/QtMultimedia/typesystem_multimedia_common.xml @@ -77,7 +77,7 @@ </modify-function> --> <!-- TODO: PYSIDE-354, arrays are not supported --> - <modify-function signature="mapPlanes(QAbstractVideoBuffer::MapMode,int*,Array,Array)" remove="all"/> + <modify-function signature="mapPlanes(QAbstractVideoBuffer::MapMode,int*,int[4],uchar*[4])" remove="all"/> </object-type> <object-type name="QAbstractVideoSurface"> <enum-type name="Error"/> diff --git a/sources/pyside2/PySide2/QtOpenGL/typesystem_opengl.xml b/sources/pyside2/PySide2/QtOpenGL/typesystem_opengl.xml index d5c701b6f..8dea5d67c 100644 --- a/sources/pyside2/PySide2/QtOpenGL/typesystem_opengl.xml +++ b/sources/pyside2/PySide2/QtOpenGL/typesystem_opengl.xml @@ -59,6 +59,16 @@ <rejection class="QGLColormap::QGLColormapData"/> <rejection class="QGLContext" field-name="currentCtx"/> + <rejection class="^QGL.*$" argument-type="^GLboolean( const)?\*$"/> + <rejection class="^QGL.*$" argument-type="^GLchar( const)?\*$"/> + <rejection class="^QGL.*$" argument-type="GLchar *const const*"/> + <rejection class="^QGL.*$" argument-type="^GLenum( const)?\*$"/> + <rejection class="^QGL.*$" argument-type="^GLfloat( const)?\*$"/> + <rejection class="^QGL.*$" argument-type="^GLfloat( const)?\[.*$"/> + <rejection class="^QGL.*$" argument-type="^GLdouble( const)?\*$"/> + <rejection class="^QGL.*$" argument-type="GLintptr"/> + <rejection class="^QGL.*$" argument-type="^GLint64( const)?\*$"/> + <rejection class="^QGL.*$" argument-type="^GLsizei( const)?\*$"/> <namespace-type name="QGL"> <enum-type name="FormatOption" flags="FormatOptions" force-integer="yes"/> @@ -681,8 +691,6 @@ <!-- ### --> <!-- ### Use QMatrixZxY overloads --> - <modify-function signature="setUniformValue(int,Array)" remove="all" /> - <modify-function signature="setUniformValue(const char*,Array)" remove="all" /> <modify-function signature="setAttributeValue(int,const GLfloat*,int,int)" remove="all" /> <modify-function signature="setAttributeValue(const char*,const GLfloat*,int,int)" remove="all" /> <modify-function signature="setAttributeArray(int, GLenum, const void*, int, int)" remove="all" since="4.7" /> diff --git a/sources/pyside2/PySide2/QtTest/typesystem_test.xml b/sources/pyside2/PySide2/QtTest/typesystem_test.xml index 2c45ab3da..b30a6b5da 100644 --- a/sources/pyside2/PySide2/QtTest/typesystem_test.xml +++ b/sources/pyside2/PySide2/QtTest/typesystem_test.xml @@ -51,6 +51,7 @@ <rejection class="QTest" function-name="qCompare<double,qreal>"/> <rejection class="QTest" function-name="qCompare<qreal,double>"/> <rejection class="QTest" function-name="qCompare"/> + <rejection class="QTest" function-name="qInit"/> <rejection class="QTest" function-name="qVerify"/> <rejection class="QTest" function-name="qSleep"/> <rejection class="QTest" function-name="toHexRepresentation"/> diff --git a/sources/pyside2/PySide2/QtWidgets/typesystem_widgets_common.xml b/sources/pyside2/PySide2/QtWidgets/typesystem_widgets_common.xml index 59412699c..7c16781c8 100644 --- a/sources/pyside2/PySide2/QtWidgets/typesystem_widgets_common.xml +++ b/sources/pyside2/PySide2/QtWidgets/typesystem_widgets_common.xml @@ -1576,7 +1576,7 @@ </modify-function> <!-- TODO: Support conversions on virtual function --> - <modify-function signature="drawItems(QPainter*, int, QGraphicsItem**, const QStyleOptionGraphicsItem*)"> + <modify-function signature="drawItems(QPainter*, int, QGraphicsItem*[], const QStyleOptionGraphicsItem[])"> <modify-argument index="2"> <remove-argument/> <conversion-rule class="native"> @@ -1688,7 +1688,7 @@ <enum-type name="SceneLayer" flags="SceneLayers"/> <!-- Qt5: note: this was called 'obsolete'. Is that true? --> - <modify-function signature="drawItems(QPainter*,int,QGraphicsItem**,const QStyleOptionGraphicsItem*,QWidget*)" remove="all" /> + <modify-function signature="drawItems(QPainter*,int,QGraphicsItem*[],const QStyleOptionGraphicsItem[],QWidget*)" remove="all" /> <modify-function signature="createItemGroup(const QList<QGraphicsItem*>&)"> <modify-argument index="1"> diff --git a/sources/pyside2/PySide2/__init__.py.in b/sources/pyside2/PySide2/__init__.py.in index 99deb35e2..ffe017587 100644 --- a/sources/pyside2/PySide2/__init__.py.in +++ b/sources/pyside2/PySide2/__init__.py.in @@ -4,6 +4,10 @@ __all__ = list("Qt" + body for body in __version__ = "@BINDING_API_VERSION_FULL@" __version_info__ = (@BINDING_API_MAJOR_VERSION@, @BINDING_API_MINOR_VERSION@, @BINDING_API_MICRO_VERSION@, "@BINDING_API_RELEASE_LEVEL@", @BINDING_API_SERIAL@) +@PYSIDE_BUILD_DATE@ +@PYSIDE_BUILD_COMMIT_DATE@ +@PYSIDE_BUILD_COMMIT_HASH@ +@PYSIDE_BUILD_COMMIT_HASH_DESCRIBED@ def _setupQtDirectories(): import sys diff --git a/sources/pyside2/tests/QtCore/CMakeLists.txt b/sources/pyside2/tests/QtCore/CMakeLists.txt index f7eb86d16..b8b0ec143 100644 --- a/sources/pyside2/tests/QtCore/CMakeLists.txt +++ b/sources/pyside2/tests/QtCore/CMakeLists.txt @@ -102,6 +102,7 @@ PYSIDE_TEST(qthread_signal_test.py) PYSIDE_TEST(qthread_test.py) PYSIDE_TEST(qtimer_singleshot_test.py) PYSIDE_TEST(qtimer_timeout_test.py) +PYSIDE_TEST(qtimezone_test.py) PYSIDE_TEST(qtnamespace_test.py) PYSIDE_TEST(qurl_test.py) PYSIDE_TEST(qurlquery_test.py) diff --git a/sources/pyside2/tests/QtCore/qtimezone_test.py b/sources/pyside2/tests/QtCore/qtimezone_test.py new file mode 100644 index 000000000..e0bebdfbf --- /dev/null +++ b/sources/pyside2/tests/QtCore/qtimezone_test.py @@ -0,0 +1,43 @@ +############################################################################# +## +## Copyright (C) 2017 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the test suite of PySide2. +## +## $QT_BEGIN_LICENSE:GPL-EXCEPT$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 3 as published by the Free Software +## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +import unittest + +from PySide2.QtCore import QTimeZone + +class TestQTimeZone (unittest.TestCase): + def testTimeZone(self): + id = 'Europe/Berlin' + timeZone = QTimeZone(id) + self.assertTrue(timeZone.isValid()) + self.assertEqual(timeZone.id(), id) + name = timeZone.displayName(QTimeZone.GenericTime, QTimeZone.DefaultName) + self.assertTrue(name) + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp b/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp index afdc84e46..5739643f2 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp +++ b/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp @@ -1990,7 +1990,8 @@ AbstractMetaFunction* AbstractMetaBuilderPrivate::traverseFunction(const AddedFu } metaFunction->setOriginalAttributes(metaFunction->attributes()); - fixArgumentNames(metaFunction); + if (!metaArguments.isEmpty()) + fixArgumentNames(metaFunction, metaFunction->modifications(m_currentClass)); if (metaClass) { const AbstractMetaArgumentList fargs = metaFunction->arguments(); @@ -2018,11 +2019,8 @@ AbstractMetaFunction* AbstractMetaBuilderPrivate::traverseFunction(const AddedFu return metaFunction; } -void AbstractMetaBuilderPrivate::fixArgumentNames(AbstractMetaFunction *func) +void AbstractMetaBuilderPrivate::fixArgumentNames(AbstractMetaFunction *func, const FunctionModificationList &mods) { - if (func->arguments().isEmpty()) - return; - const FunctionModificationList &mods = func->modifications(m_currentClass); for (const FunctionModification &mod : mods) { for (const ArgumentModification &argMod : mod.argument_mods) { if (!argMod.renamed_to.isEmpty()) { @@ -2108,6 +2106,44 @@ static inline AbstractMetaFunction::FunctionType functionTypeFromCodeModel(CodeM return result; } +static inline QString msgCannotSetArrayUsage(const QString &function, int i, const QString &reason) +{ + return function + QLatin1String(": Cannot use parameter ") + QString::number(i + 1) + + QLatin1String(" as an array: ") + reason; +} + +bool AbstractMetaBuilderPrivate::setArrayArgumentType(AbstractMetaFunction *func, + const FunctionModelItem &functionItem, + int i) +{ + if (i < 0 || i >= func->arguments().size()) { + qCWarning(lcShiboken).noquote() + << msgCannotSetArrayUsage(func->minimalSignature(), i, + QLatin1String("Index out of range.")); + return false; + } + AbstractMetaType *metaType = func->arguments().at(i)->type(); + if (metaType->indirections() == 0) { + qCWarning(lcShiboken).noquote() + << msgCannotSetArrayUsage(func->minimalSignature(), i, + QLatin1String("Type does not have indirections.")); + return false; + } + TypeInfo elementType = functionItem->arguments().at(i)->type(); + elementType.setIndirections(elementType.indirections() - 1); + bool ok; + AbstractMetaType *element = translateType(elementType, &ok); + if (element == nullptr || !ok) { + qCWarning(lcShiboken).noquote() + << msgCannotSetArrayUsage(func->minimalSignature(), i, + QLatin1String("Cannot translate element type ") + elementType.toString()); + return false; + } + metaType->setArrayElementType(element); + metaType->setTypeUsagePattern(AbstractMetaType::NativePointerAsArrayPattern); + return true; +} + AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(FunctionModelItem functionItem) { if (!functionItem->templateParameters().isEmpty()) @@ -2322,7 +2358,16 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(FunctionModel } - fixArgumentNames(metaFunction); + if (!metaArguments.isEmpty()) { + const FunctionModificationList &mods = metaFunction->modifications(m_currentClass); + fixArgumentNames(metaFunction, mods); + for (const FunctionModification &mod : mods) { + for (const ArgumentModification &argMod : mod.argument_mods) { + if (argMod.array) + setArrayArgumentType(metaFunction, functionItem, argMod.index - 1); + } + } + } // Determine class special functions if (m_currentClass && metaFunction->arguments().size() == 1) { @@ -2474,45 +2519,36 @@ AbstractMetaType *AbstractMetaBuilderPrivate::translateType(const TypeInfo &_typ return 0; } - // 2. Handle pointers specified as arrays with unspecified size - bool arrayOfUnspecifiedSize = false; if (typeInfo.arrays.size() > 0) { - arrayOfUnspecifiedSize = true; - for (int i = 0; i < typeInfo.arrays.size(); ++i) - arrayOfUnspecifiedSize = arrayOfUnspecifiedSize && typeInfo.arrays.at(i).isEmpty(); - - if (!arrayOfUnspecifiedSize) { - TypeInfo newInfo; - //newInfo.setArguments(typei.arguments()); - newInfo.setIndirections(typei.indirections()); - newInfo.setConstant(typei.isConstant()); - newInfo.setFunctionPointer(typei.isFunctionPointer()); - newInfo.setQualifiedName(typei.qualifiedName()); - newInfo.setReferenceType(typei.referenceType()); - newInfo.setVolatile(typei.isVolatile()); - - AbstractMetaType* elementType = translateType(newInfo, ok); - if (!(*ok)) - return 0; + TypeInfo newInfo; + //newInfo.setArguments(typei.arguments()); + newInfo.setIndirections(typei.indirections()); + newInfo.setConstant(typei.isConstant()); + newInfo.setFunctionPointer(typei.isFunctionPointer()); + newInfo.setQualifiedName(typei.qualifiedName()); + newInfo.setReferenceType(typei.referenceType()); + newInfo.setVolatile(typei.isVolatile()); + + AbstractMetaType* elementType = translateType(newInfo, ok); + if (!(*ok)) + return 0; - for (int i = typeInfo.arrays.size() - 1; i >= 0; --i) { - QString s = typeInfo.arrays.at(i); + for (int i = typeInfo.arrays.size() - 1; i >= 0; --i) { + AbstractMetaType *arrayType = q->createMetaType(); + arrayType->setArrayElementType(elementType); + if (!typeInfo.arrays.at(i).isEmpty()) { bool _ok; - int elems = findOutValueFromString(s, _ok); - - AbstractMetaType *arrayType = q->createMetaType(); - arrayType->setArrayElementCount(elems); - arrayType->setArrayElementType(elementType); - arrayType->setTypeEntry(new ArrayTypeEntry(elementType->typeEntry() , elementType->typeEntry()->version())); - decideUsagePattern(arrayType); - - elementType = arrayType; + const int elems = findOutValueFromString(typeInfo.arrays.at(i), _ok); + if (_ok) + arrayType->setArrayElementCount(elems); } + arrayType->setTypeEntry(new ArrayTypeEntry(elementType->typeEntry() , elementType->typeEntry()->version())); + decideUsagePattern(arrayType); - return elementType; - } else { - typeInfo.indirections += typeInfo.arrays.size(); + elementType = arrayType; } + + return elementType; } QStringList qualifierList = typeInfo.qualified_name; diff --git a/sources/shiboken2/ApiExtractor/abstractmetabuilder_p.h b/sources/shiboken2/ApiExtractor/abstractmetabuilder_p.h index 8c4e4b185..71b69473e 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetabuilder_p.h +++ b/sources/shiboken2/ApiExtractor/abstractmetabuilder_p.h @@ -155,7 +155,10 @@ public: void sortLists(); AbstractMetaArgumentList reverseList(const AbstractMetaArgumentList &list); void setInclude(TypeEntry *te, const QString &fileName) const; - void fixArgumentNames(AbstractMetaFunction *func); + void fixArgumentNames(AbstractMetaFunction *func, const FunctionModificationList &mods); + bool setArrayArgumentType(AbstractMetaFunction *func, + const FunctionModelItem &functionItem, int i); + void fillAddedFunctions(AbstractMetaClass *metaClass); AbstractMetaBuilder *q; diff --git a/sources/shiboken2/ApiExtractor/abstractmetalang.cpp b/sources/shiboken2/ApiExtractor/abstractmetalang.cpp index 482efdaa0..d38eb8587 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetalang.cpp +++ b/sources/shiboken2/ApiExtractor/abstractmetalang.cpp @@ -91,7 +91,7 @@ QDebug operator<<(QDebug d, const AbstractMetaVariable *av) AbstractMetaType::AbstractMetaType() :m_typeEntry(0), - m_arrayElementCount(0), + m_arrayElementCount(-1), m_arrayElementType(0), m_originalTemplateType(0), m_pattern(InvalidPattern), @@ -147,6 +147,26 @@ AbstractMetaType *AbstractMetaType::copy() const return cpy; } +AbstractMetaTypeCList AbstractMetaType::nestedArrayTypes() const +{ + AbstractMetaTypeCList result; + switch (m_pattern) { + case ArrayPattern: + for (const AbstractMetaType *t = this; t->typeUsagePattern() == ArrayPattern; ) { + const AbstractMetaType *elt = t->arrayElementType(); + result.append(elt); + t = elt; + } + break; + case NativePointerAsArrayPattern: + result.append(m_arrayElementType); + break; + default: + break; + } + return result; +} + QString AbstractMetaType::cppSignature() const { if (m_cachedCppSignature.isEmpty()) @@ -2545,13 +2565,32 @@ void AbstractMetaClass::fixFunctions() setFunctions(funcs); } +static inline QString formatArraySize(int e) +{ + QString result; + result += QLatin1Char('['); + if (e >= 0) + result += QString::number(e); + result += QLatin1Char(']'); + return result; +} QString AbstractMetaType::formatSignature(bool minimal) const { QString result; if (isConstant()) result += QLatin1String("const "); - result += typeEntry()->qualifiedCppName(); + if (isArray()) { + // Build nested array dimensions a[2][3] in correct order + result += m_arrayElementType->minimalSignature(); + const int arrayPos = result.indexOf(QLatin1Char('[')); + if (arrayPos != -1) + result.insert(arrayPos, formatArraySize(m_arrayElementCount)); + else + result.append(formatArraySize(m_arrayElementCount)); + } else { + result += typeEntry()->qualifiedCppName(); + } if (!m_instantiations.isEmpty()) { result += QLatin1Char('<'); if (minimal) @@ -2586,6 +2625,11 @@ bool AbstractMetaType::hasNativeId() const return (isQObject() || isValue() || isObject()) && typeEntry()->isNativeIdBased(); } +bool AbstractMetaType::isCppPrimitive() const +{ + return m_pattern == PrimitivePattern && m_typeEntry->isCppPrimitive(); +} + bool AbstractMetaType::isTargetLangEnum() const { return isEnum() && !static_cast<const EnumTypeEntry *>(typeEntry())->forceInteger(); diff --git a/sources/shiboken2/ApiExtractor/abstractmetalang.h b/sources/shiboken2/ApiExtractor/abstractmetalang.h index 921df4300..0529bed24 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetalang.h +++ b/sources/shiboken2/ApiExtractor/abstractmetalang.h @@ -326,6 +326,7 @@ public: QObjectPattern, ValuePointerPattern, NativePointerPattern, + NativePointerAsArrayPattern, // "int*" as "int[]" ContainerPattern, SmartPointerPattern, VariantPattern, @@ -399,6 +400,8 @@ public: return m_pattern == PrimitivePattern; } + bool isCppPrimitive() const; + // returns true if the type is used as an enum bool isEnum() const { @@ -558,6 +561,8 @@ public: m_arrayElementType = t; } + AbstractMetaTypeCList nestedArrayTypes() const; + QString cppSignature() const; AbstractMetaType *copy() const; diff --git a/sources/shiboken2/ApiExtractor/abstractmetalang_typedefs.h b/sources/shiboken2/ApiExtractor/abstractmetalang_typedefs.h index da8369895..6522ba2dd 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetalang_typedefs.h +++ b/sources/shiboken2/ApiExtractor/abstractmetalang_typedefs.h @@ -46,5 +46,6 @@ typedef QVector<AbstractMetaEnumValue *> AbstractMetaEnumValueList; typedef QVector<AbstractMetaField *> AbstractMetaFieldList; typedef QVector<AbstractMetaFunction *> AbstractMetaFunctionList; typedef QVector<AbstractMetaType *> AbstractMetaTypeList; +typedef QVector<const AbstractMetaType *> AbstractMetaTypeCList; #endif // ABSTRACTMETALANG_TYPEDEFS_H diff --git a/sources/shiboken2/ApiExtractor/tests/testarrayargument.cpp b/sources/shiboken2/ApiExtractor/tests/testarrayargument.cpp index 4d46d44bc..408c51461 100644 --- a/sources/shiboken2/ApiExtractor/tests/testarrayargument.cpp +++ b/sources/shiboken2/ApiExtractor/tests/testarrayargument.cpp @@ -58,6 +58,45 @@ void TestArrayArgument::testArrayArgumentWithSizeDefinedByInteger() QCOMPARE(arg->type()->arrayElementType()->name(), QLatin1String("double")); } +static QString functionMinimalSignature(const AbstractMetaClass *c, const QString &name) +{ + const AbstractMetaFunction *f = c->findFunction(name); + return f ? f->minimalSignature() : QString(); +} + +void TestArrayArgument::testArraySignature() +{ + const char cppCode[] ="\ + struct A {\n\ + void mi1(int arg[5]);\n\ + void mi1c(const int arg[5]);\n\ + void mi1cu(const int arg[]);\n\ + void muc2(unsigned char *arg[2][3]);\n\ + void mc2c(const char *arg[5][6]);\n\ + };\n"; + const char xmlCode[] = "\ + <typesystem package='Foo'>\n\ + <primitive-type name='char'/>\n\ + <primitive-type name='unsigned char'/>\n\ + <primitive-type name='int'/>\n\ + <object-type name='A'/>\n\ + </typesystem>\n"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(!builder.isNull()); + const AbstractMetaClass *classA = AbstractMetaClass::findClass(builder->classes(), QLatin1String("A")); + QCOMPARE(functionMinimalSignature(classA, QLatin1String("mi1")), + QLatin1String("mi1(int[5])")); + QCOMPARE(functionMinimalSignature(classA, QLatin1String("mi1c")), + QLatin1String("mi1c(const int[5])")); + QCOMPARE(functionMinimalSignature(classA, QLatin1String("mi1cu")), + QLatin1String("mi1cu(const int[])")); + QCOMPARE(functionMinimalSignature(classA, QLatin1String("muc2")), + QLatin1String("muc2(unsigned char*[2][3])")); + QCOMPARE(functionMinimalSignature(classA, QLatin1String("mc2c")), + QLatin1String("mc2c(const char*[5][6])")); +} + void TestArrayArgument::testArrayArgumentWithSizeDefinedByEnumValue() { const char* cppCode ="\ diff --git a/sources/shiboken2/ApiExtractor/tests/testarrayargument.h b/sources/shiboken2/ApiExtractor/tests/testarrayargument.h index b50232ef4..45ca8e655 100644 --- a/sources/shiboken2/ApiExtractor/tests/testarrayargument.h +++ b/sources/shiboken2/ApiExtractor/tests/testarrayargument.h @@ -35,6 +35,7 @@ class TestArrayArgument : public QObject Q_OBJECT private slots: void testArrayArgumentWithSizeDefinedByInteger(); + void testArraySignature(); void testArrayArgumentWithSizeDefinedByEnumValue(); void testArrayArgumentWithSizeDefinedByEnumValueFromGlobalEnum(); }; diff --git a/sources/shiboken2/ApiExtractor/typesystem.cpp b/sources/shiboken2/ApiExtractor/typesystem.cpp index 9adc5107b..13664c336 100644 --- a/sources/shiboken2/ApiExtractor/typesystem.cpp +++ b/sources/shiboken2/ApiExtractor/typesystem.cpp @@ -176,6 +176,7 @@ Handler::Handler(TypeDatabase* database, bool generate) tagNames.insert(QLatin1String("no-null-pointer"), StackElement::NoNullPointers); tagNames.insert(QLatin1String("reference-count"), StackElement::ReferenceCount); tagNames.insert(QLatin1String("parent"), StackElement::ParentOwner); + tagNames.insert(QLatin1String("array"), StackElement::Array); tagNames.insert(QLatin1String("inject-documentation"), StackElement::InjectDocumentation); tagNames.insert(QLatin1String("modify-documentation"), StackElement::ModifyDocumentation); tagNames.insert(QLatin1String("add-function"), StackElement::AddFunction); @@ -1283,6 +1284,9 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts case StackElement::ParentOwner: attributes.insert(QLatin1String("index"), QString()); attributes.insert(QLatin1String("action"), QString()); + break; + case StackElement::Array: + break; default: { }; }; @@ -1875,8 +1879,13 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts m_contextStack.top()->functionMods.last().argument_mods.last().owner = ao; } break; - - + case StackElement::Array: + if (topElement.type != StackElement::ModifyArgument) { + m_error = QLatin1String("array must be child of modify-argument"); + return false; + } + m_contextStack.top()->functionMods.last().argument_mods.last().array = true; + break; case StackElement::InjectCode: { if (!(topElement.type & StackElement::ComplexTypeEntryMask) && (topElement.type != StackElement::AddFunction) diff --git a/sources/shiboken2/ApiExtractor/typesystem.h b/sources/shiboken2/ApiExtractor/typesystem.h index b75da48ba..6c65adbe1 100644 --- a/sources/shiboken2/ApiExtractor/typesystem.h +++ b/sources/shiboken2/ApiExtractor/typesystem.h @@ -211,16 +211,17 @@ public: struct ArgumentModification { ArgumentModification() : removedDefaultExpression(false), removed(false), - noNullPointers(false), index(-1), version(0) {} + noNullPointers(false), array(false), index(-1), version(0) {} ArgumentModification(int idx, double vr) : removedDefaultExpression(false), removed(false), - noNullPointers(false), index(idx), version(vr) {} + noNullPointers(false), array(false), index(idx), version(vr) {} // Should the default expression be removed? uint removedDefaultExpression : 1; uint removed : 1; uint noNullPointers : 1; uint resetAfterUse : 1; + uint array : 1; // consider "int*" to be "int[]" // The index of this argument int index; diff --git a/sources/shiboken2/ApiExtractor/typesystem_p.h b/sources/shiboken2/ApiExtractor/typesystem_p.h index f2105a631..d485aad42 100644 --- a/sources/shiboken2/ApiExtractor/typesystem_p.h +++ b/sources/shiboken2/ApiExtractor/typesystem_p.h @@ -107,6 +107,7 @@ class StackElement NoNullPointers = 0x40000000, ReferenceCount = 0x80000000, ParentOwner = 0x90000000, + Array = 0xA0000000, ArgumentModifiers = 0xff000000 }; diff --git a/sources/shiboken2/generator/generator.cpp b/sources/shiboken2/generator/generator.cpp index f60b195f2..a8e8735bb 100644 --- a/sources/shiboken2/generator/generator.cpp +++ b/sources/shiboken2/generator/generator.cpp @@ -515,9 +515,12 @@ bool Generator::isVoidPointer(const AbstractMetaType* type) QString Generator::getFullTypeName(const TypeEntry* type) const { - return type->isCppPrimitive() - ? type->qualifiedCppName() - : (QLatin1String("::") + type->qualifiedCppName()); + QString result = type->qualifiedCppName(); + if (type->isArray()) + type = static_cast<const ArrayTypeEntry *>(type)->nestedTypeEntry(); + if (!type->isCppPrimitive()) + result.prepend(QLatin1String("::")); + return result; } QString Generator::getFullTypeName(const AbstractMetaType* type) const diff --git a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp index 91ed7eec5..f7c47fd05 100644 --- a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp +++ b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp @@ -2116,6 +2116,7 @@ static void checkTypeViability(const AbstractMetaFunction* func, const AbstractM if (!type || !type->typeEntry()->isPrimitive() || type->indirections() == 0 + || (type->indirections() == 1 && type->typeUsagePattern() == AbstractMetaType::NativePointerAsArrayPattern) || ShibokenGenerator::isCString(type) || func->argumentRemoved(argIdx) || !func->typeReplaced(argIdx).isEmpty() @@ -2206,6 +2207,23 @@ const AbstractMetaType* CppGenerator::getArgumentType(const AbstractMetaFunction return argType; } +static inline QString arrayHandleType(const AbstractMetaTypeCList &nestedArrayTypes) +{ + switch (nestedArrayTypes.size()) { + case 1: + return QStringLiteral("Shiboken::Conversions::ArrayHandle<") + + nestedArrayTypes.constLast()->minimalSignature() + + QLatin1Char('>'); + case 2: + return QStringLiteral("Shiboken::Conversions::Array2Handle<") + + nestedArrayTypes.constLast()->minimalSignature() + + QStringLiteral(", ") + + QString::number(nestedArrayTypes.constFirst()->arrayElementCount()) + + QLatin1Char('>'); + } + return QString(); +} + void CppGenerator::writePythonToCppTypeConversion(QTextStream& s, const AbstractMetaType* type, const QString& pyIn, @@ -2227,7 +2245,13 @@ void CppGenerator::writePythonToCppTypeConversion(QTextStream& s, && !isCppPrimitive(type) && isNotContainerEnumOrFlags && !(treatAsPointer || isPointerOrObjectType); - QString typeName = getFullTypeNameWithoutModifiers(type); + + const AbstractMetaTypeCList nestedArrayTypes = type->nestedArrayTypes(); + const bool isCppPrimitiveArray = !nestedArrayTypes.isEmpty() + && nestedArrayTypes.constLast()->isCppPrimitive(); + QString typeName = isCppPrimitiveArray + ? arrayHandleType(nestedArrayTypes) + : getFullTypeNameWithoutModifiers(type); bool isProtectedEnum = false; @@ -2244,7 +2268,9 @@ void CppGenerator::writePythonToCppTypeConversion(QTextStream& s, } s << INDENT << typeName; - if (treatAsPointer || isPointerOrObjectType) { + if (isCppPrimitiveArray) { + s << ' ' << cppOut; + } else if (treatAsPointer || isPointerOrObjectType) { s << "* " << cppOut; if (!defaultValue.isEmpty()) s << " = " << defaultValue; diff --git a/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp b/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp index 2693ecf40..b6242c57f 100644 --- a/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp +++ b/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp @@ -779,6 +779,13 @@ QString ShibokenGenerator::converterObject(const AbstractMetaType* type) return QLatin1String("Shiboken::Conversions::PrimitiveTypeConverter<const char*>()"); if (isVoidPointer(type)) return QLatin1String("Shiboken::Conversions::PrimitiveTypeConverter<void*>()"); + const AbstractMetaTypeCList nestedArrayTypes = type->nestedArrayTypes(); + if (!nestedArrayTypes.isEmpty() && nestedArrayTypes.constLast()->isCppPrimitive()) { + return QStringLiteral("Shiboken::Conversions::ArrayTypeConverter<") + + nestedArrayTypes.constLast()->minimalSignature() + + QLatin1String(">(") + QString::number(nestedArrayTypes.size()) + + QLatin1Char(')'); + } if (type->typeEntry()->isContainer()) { return convertersVariableName(type->typeEntry()->targetLangPackage()) + QLatin1Char('[') + getTypeIndexVariableName(type) + QLatin1Char(']'); @@ -1232,8 +1239,8 @@ QString ShibokenGenerator::cpythonIsConvertibleFunction(const AbstractMetaType* return customCheck; } + QString result = QLatin1String("Shiboken::Conversions::"); if (isWrapperType(metaType)) { - QString result = QLatin1String("Shiboken::Conversions::"); if (isPointer(metaType) || isValueTypeWithCopyConstructorOnly(metaType)) result += QLatin1String("isPythonToCppPointerConvertible"); else if (metaType->referenceType() == LValueReference) @@ -1244,8 +1251,18 @@ QString ShibokenGenerator::cpythonIsConvertibleFunction(const AbstractMetaType* + cpythonTypeNameExt(metaType) + QLatin1String("), "); return result; } - return QStringLiteral("Shiboken::Conversions::isPythonToCppConvertible(%1, ") - .arg(converterObject(metaType)); + result += QLatin1String("isPythonToCppConvertible(") + converterObject(metaType); + // Write out array sizes if known + const AbstractMetaTypeCList nestedArrayTypes = metaType->nestedArrayTypes(); + if (!nestedArrayTypes.isEmpty() && nestedArrayTypes.constLast()->isCppPrimitive()) { + const int dim1 = metaType->arrayElementCount(); + const int dim2 = nestedArrayTypes.constFirst()->isArray() + ? nestedArrayTypes.constFirst()->arrayElementCount() : -1; + result += QLatin1String(", ") + QString::number(dim1) + + QLatin1String(", ") + QString::number(dim2); + } + result += QLatin1String(", "); + return result; } QString ShibokenGenerator::cpythonIsConvertibleFunction(const AbstractMetaArgument *metaArg, bool genericNumberType) @@ -1330,8 +1347,12 @@ QString ShibokenGenerator::argumentString(const AbstractMetaFunction *func, arg = modified_type.replace(QLatin1Char('$'), QLatin1Char('.')); if (!(options & Generator::SkipName)) { - arg += QLatin1Char(' '); - arg += argument->name(); + // "int a", "int a[]" + const int arrayPos = arg.indexOf(QLatin1Char('[')); + if (arrayPos != -1) + arg.insert(arrayPos, QLatin1Char(' ') + argument->name()); + else + arg.append(QLatin1Char(' ') + argument->name()); } if ((options & Generator::SkipDefaultValues) != Generator::SkipDefaultValues && diff --git a/sources/shiboken2/libshiboken/CMakeLists.txt b/sources/shiboken2/libshiboken/CMakeLists.txt index 5363ccf42..06d6330f5 100644 --- a/sources/shiboken2/libshiboken/CMakeLists.txt +++ b/sources/shiboken2/libshiboken/CMakeLists.txt @@ -1,5 +1,22 @@ project(libshiboken) +macro(get_numpy_location) + execute_process( + COMMAND ${PYTHON_EXECUTABLE} -c "if True: + import sys + import os + numpy = '' + for p in sys.path: + if 'site-' in p: + numpy = os.path.join(p, 'numpy') + if os.path.exists(numpy): + print(os.path.realpath(numpy)) + break" + OUTPUT_VARIABLE PYTHON_NUMPY_LOCATION + OUTPUT_STRIP_TRAILING_WHITESPACE) + message("PYTHON_NUMPY_LOCATION: " ${PYTHON_NUMPY_LOCATION}) +endmacro() + option(ENABLE_VERSION_SUFFIX "Used to use current version in suffix to generated files. This is used to allow multiples versions installed simultaneous." FALSE) if(ENABLE_VERSION_SUFFIX) set(shiboken2_SUFFIX "-${shiboken_MAJOR_VERSION}.${shiboken_MINOR_VERSION}") @@ -21,6 +38,7 @@ basewrapper.cpp debugfreehook.cpp gilstate.cpp helper.cpp +sbkarrayconverter.cpp sbkconverter.cpp sbkenum.cpp sbkmodule.cpp @@ -31,9 +49,19 @@ typeresolver.cpp shibokenbuffer.cpp ) -include_directories(${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_CURRENT_BINARY_DIR} - ${SBK_PYTHON_INCLUDE_DIR}) +get_numpy_location() + +set(libshiboken_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${SBK_PYTHON_INCLUDE_DIR}) + +if (NOT "${PYTHON_NUMPY_LOCATION}" STREQUAL "") + set(libshiboken_INCLUDES ${libshiboken_INCLUDES} ${PYTHON_NUMPY_LOCATION}/core/include) + set(libshiboken_SRC ${libshiboken_SRC} sbknumpyarrayconverter.cpp) + add_definitions(-DHAVE_NUMPY -DNPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION) +endif() + +set(APIEXTRACTOR_EXTRA_INCLUDES ${APIEXTRACTOR_EXTRA_INCLUDES} ${LIBXSLT_INCLUDE_DIR} ${LIBXML2_INCLUDE_DIR}) + +include_directories(${libshiboken_INCLUDES}) add_library(libshiboken SHARED ${libshiboken_SRC}) target_link_libraries(libshiboken ${SBK_PYTHON_LIBRARIES}) set_target_properties(libshiboken PROPERTIES OUTPUT_NAME "shiboken2${shiboken2_SUFFIX}${PYTHON_SHARED_LIBRARY_SUFFIX}" @@ -48,6 +76,7 @@ install(FILES conversions.h gilstate.h helper.h + sbkarrayconverter.h sbkconverter.h sbkenum.h sbkmodule.h diff --git a/sources/shiboken2/libshiboken/sbkarrayconverter.cpp b/sources/shiboken2/libshiboken/sbkarrayconverter.cpp new file mode 100644 index 000000000..c22015709 --- /dev/null +++ b/sources/shiboken2/libshiboken/sbkarrayconverter.cpp @@ -0,0 +1,289 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of PySide2. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "sbkarrayconverter.h" +#include "sbkarrayconverter_p.h" +#include "helper.h" +#include "sbkconverter.h" +#include "sbkconverter_p.h" + +#include <longobject.h> +#include <floatobject.h> + +#include <algorithm> + +static SbkArrayConverter *ArrayTypeConverters[Shiboken::Conversions::SBK_ARRAY_IDX_SIZE] [2] = {}; + +namespace Shiboken { +namespace Conversions { + +// Check whether Predicate is true for all elements of a sequence +template <class Predicate> +static bool sequenceAllOf(PyObject *pyIn, Predicate p) +{ + const Py_ssize_t size = PySequence_Size(pyIn); + for (Py_ssize_t i = 0; i < size; ++i) { + PyObject *item = PySequence_GetItem(pyIn, i); + const bool ok = p(item); + Py_XDECREF(item); + if (!ok) + return false; + } + return true; +} + +// Convert a sequence to output iterator +template <class T, class Converter> +inline void convertPySequence(PyObject *pyIn, Converter c, T *out) +{ + const Py_ssize_t size = PySequence_Size(pyIn); + for (Py_ssize_t i = 0; i < size; ++i) { + PyObject *item = PySequence_GetItem(pyIn, i); + *out++ = c(item); + Py_XDECREF(item); + } +} + +// Internal, for usage by numpy +SbkArrayConverter *createArrayConverter(IsArrayConvertibleToCppFunc toCppCheckFunc) +{ + SbkArrayConverter *result = new SbkArrayConverter; + result->toCppConversions.push_back(toCppCheckFunc); + return result; +} + +static PythonToCppFunc unimplementedArrayCheck(PyObject *, int, int) +{ + warning(PyExc_RuntimeWarning, 0, "SbkConverter: Unimplemented C++ array type."); + return nullptr; +} + +SbkArrayConverter *unimplementedArrayConverter() +{ + static SbkArrayConverter *result = createArrayConverter(unimplementedArrayCheck); + return result; +} + +// Integers + +static inline bool intCheck(PyObject *pyIn) +{ +#ifdef IS_PY3K + return PyLong_Check(pyIn); +#else + return PyInt_Check(pyIn); +#endif +} + +static short toShort(PyObject *pyIn) { return short(PyLong_AsLong(pyIn)); } + +static void sequenceToCppShortArray(PyObject *pyIn, void *cppOut) +{ + ArrayHandle<short> *handle = reinterpret_cast<ArrayHandle<short> *>(cppOut); + handle->allocate(PySequence_Size(pyIn)); + convertPySequence(pyIn, toShort, handle->data()); +} + +static inline bool sequenceSizeCheck(PyObject *pyIn, int expectedSize = -1) +{ + if (expectedSize >= 0) { + const int size = int(PySequence_Size(pyIn)); + if (size < expectedSize) { + warning(PyExc_RuntimeWarning, 0, "A sequence of size %d was passed to a function that expects %d.", + size, expectedSize); + return false; + } + } + return true; +} + +static inline bool intArrayCheck(PyObject *pyIn, int expectedSize = -1) +{ + return PySequence_Check(pyIn) && sequenceAllOf(pyIn, intCheck) + && sequenceSizeCheck(pyIn, expectedSize); +} + +static PythonToCppFunc sequenceToCppShortArrayCheck(PyObject *pyIn, int dim1, int /* dim2 */) +{ + return intArrayCheck(pyIn, dim1) ? sequenceToCppShortArray : nullptr; +} + +static short toUnsignedShort(PyObject *pyIn) { return static_cast<unsigned short>(PyLong_AsUnsignedLong(pyIn)); } + +static void sequenceToCppUnsignedShortArray(PyObject *pyIn, void *cppOut) +{ + ArrayHandle<unsigned short> *handle = reinterpret_cast<ArrayHandle<unsigned short> *>(cppOut); + handle->allocate(PySequence_Size(pyIn)); + convertPySequence(pyIn, toUnsignedShort, handle->data()); +} + +static PythonToCppFunc sequenceToCppUnsignedShortArrayCheck(PyObject *pyIn, int dim1, int /* dim2 */) +{ + return intArrayCheck(pyIn, dim1) ? sequenceToCppUnsignedShortArray : nullptr; +} + +static void sequenceToCppIntArray(PyObject *pyIn, void *cppOut) +{ + ArrayHandle<int> *handle = reinterpret_cast<ArrayHandle<int> *>(cppOut); + handle->allocate(PySequence_Size(pyIn)); + convertPySequence(pyIn, _PyLong_AsInt, handle->data()); +} + +static PythonToCppFunc sequenceToCppIntArrayCheck(PyObject *pyIn, int dim1, int /* dim2 */) +{ + return intArrayCheck(pyIn, dim1) ? sequenceToCppIntArray : nullptr; +} + +static void sequenceToCppUnsignedArray(PyObject *pyIn, void *cppOut) +{ + ArrayHandle<unsigned> *handle = reinterpret_cast<ArrayHandle<unsigned> *>(cppOut); + handle->allocate(PySequence_Size(pyIn)); + convertPySequence(pyIn, PyLong_AsUnsignedLong, handle->data()); +} + +static PythonToCppFunc sequenceToCppUnsignedArrayCheck(PyObject *pyIn, int dim1, int /* dim2 */) +{ + return intArrayCheck(pyIn, dim1) ? sequenceToCppUnsignedArray : nullptr; +} + +static void sequenceToCppLongLongArray(PyObject *pyIn, void *cppOut) +{ + ArrayHandle<long long> *handle = reinterpret_cast<ArrayHandle<long long> *>(cppOut); + handle->allocate(PySequence_Size(pyIn)); + convertPySequence(pyIn, PyLong_AsLongLong, handle->data()); +} + +static PythonToCppFunc sequenceToCppLongLongArrayCheck(PyObject *pyIn, int dim1, int /* dim2 */) +{ + return intArrayCheck(pyIn, dim1) ? sequenceToCppLongLongArray : nullptr; +} + +static void sequenceToCppUnsignedLongLongArray(PyObject *pyIn, void *cppOut) +{ + ArrayHandle<unsigned long long> *handle = reinterpret_cast<ArrayHandle<unsigned long long> *>(cppOut); + handle->allocate(PySequence_Size(pyIn)); + convertPySequence(pyIn, PyLong_AsUnsignedLongLong, handle->data()); +} + +static PythonToCppFunc sequenceToCppUnsignedLongLongArrayCheck(PyObject *pyIn, int dim1, int /* dim2 */) +{ + return intArrayCheck(pyIn, dim1) ? sequenceToCppUnsignedLongLongArray : nullptr; +} + +// Float + +static inline bool floatCheck(PyObject *pyIn) { return PyFloat_Check(pyIn); } + +static inline bool floatArrayCheck(PyObject *pyIn, int expectedSize = -1) +{ + return PySequence_Check(pyIn) && sequenceAllOf(pyIn, floatCheck) + && sequenceSizeCheck(pyIn, expectedSize); +} + +static void sequenceToCppDoubleArray(PyObject *pyIn, void *cppOut) +{ + ArrayHandle<double> *handle = reinterpret_cast<ArrayHandle<double> *>(cppOut); + handle->allocate(PySequence_Size(pyIn)); + convertPySequence(pyIn, PyFloat_AsDouble, handle->data()); +} + +static inline float pyToFloat(PyObject *pyIn) { return float(PyFloat_AsDouble(pyIn)); } + +static void sequenceToCppFloatArray(PyObject *pyIn, void *cppOut) +{ + ArrayHandle<float> *handle = reinterpret_cast<ArrayHandle<float> *>(cppOut); + handle->allocate(PySequence_Size(pyIn)); + convertPySequence(pyIn, pyToFloat, handle->data()); +} + +static PythonToCppFunc sequenceToCppFloatArrayCheck(PyObject *pyIn, int dim1, int /* dim2 */) +{ + return floatArrayCheck(pyIn, dim1) ? sequenceToCppFloatArray : nullptr; +} + +static PythonToCppFunc sequenceToCppDoubleArrayCheck(PyObject *pyIn, int dim1, int /* dim2 */) +{ + return floatArrayCheck(pyIn, dim1) ? sequenceToCppDoubleArray : nullptr; +} + +#ifdef HAVE_NUMPY +void initNumPyArrayConverters(); +#endif + +void initArrayConverters() +{ + SbkArrayConverter **start = &ArrayTypeConverters[0][0]; + std::fill(start, start + sizeof(ArrayTypeConverters) / sizeof(ArrayTypeConverters[0][0]), nullptr); + // Populate 1-dimensional sequence converters + ArrayTypeConverters[SBK_DOUBLE_ARRAY_IDX][0] = + createArrayConverter(sequenceToCppDoubleArrayCheck); + ArrayTypeConverters[SBK_FLOAT_ARRAY_IDX][0] = + createArrayConverter(sequenceToCppFloatArrayCheck); + ArrayTypeConverters[SBK_SHORT_ARRAY_IDX][0] = + createArrayConverter(sequenceToCppShortArrayCheck); + ArrayTypeConverters[SBK_UNSIGNEDSHORT_ARRAY_IDX][0] = + createArrayConverter(sequenceToCppUnsignedShortArrayCheck); + ArrayTypeConverters[SBK_INT_ARRAY_IDX][0] = + createArrayConverter(sequenceToCppIntArrayCheck); + ArrayTypeConverters[SBK_UNSIGNEDINT_ARRAY_IDX][0] = + createArrayConverter(sequenceToCppUnsignedArrayCheck); + ArrayTypeConverters[SBK_LONGLONG_ARRAY_IDX][0] = + createArrayConverter(sequenceToCppLongLongArrayCheck); + ArrayTypeConverters[SBK_UNSIGNEDLONGLONG_ARRAY_IDX][0] = + createArrayConverter(sequenceToCppUnsignedLongLongArrayCheck); + +#ifdef HAVE_NUMPY + initNumPyArrayConverters(); +#endif +} + +SbkArrayConverter *arrayTypeConverter(int index, int dimension) +{ + SbkArrayConverter *c = ArrayTypeConverters[index][dimension - 1]; + return c ? c : unimplementedArrayConverter(); +} + +// Internal, for usage by numpy +void setArrayTypeConverter(int index, int dimension, SbkArrayConverter *c) +{ + ArrayTypeConverters[index][dimension - 1] = c; +} + +} // namespace Conversions +} // namespace Shiboken diff --git a/sources/shiboken2/libshiboken/sbkarrayconverter.h b/sources/shiboken2/libshiboken/sbkarrayconverter.h new file mode 100644 index 000000000..f3d3e5f98 --- /dev/null +++ b/sources/shiboken2/libshiboken/sbkarrayconverter.h @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of PySide2. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SBKARRAYCONVERTERS_H +#define SBKARRAYCONVERTERS_H + +#include "sbkpython.h" +#include "shibokenmacros.h" + +extern "C" { +struct SbkArrayConverter; +} + +namespace Shiboken { +namespace Conversions { + +enum : int { + SBK_UNIMPLEMENTED_ARRAY_IDX, + SBK_DOUBLE_ARRAY_IDX, + SBK_FLOAT_ARRAY_IDX, + SBK_SHORT_ARRAY_IDX, + SBK_UNSIGNEDSHORT_ARRAY_IDX, + SBK_INT_ARRAY_IDX, + SBK_UNSIGNEDINT_ARRAY_IDX, + SBK_LONGLONG_ARRAY_IDX, + SBK_UNSIGNEDLONGLONG_ARRAY_IDX, + SBK_ARRAY_IDX_SIZE +}; + +/** + * ArrayHandle is the type expected by shiboken2's array converter + * functions. It provides access to array data which it may own + * (in the case of conversions from PySequence) or a flat pointer + * to internal data (in the case of array modules like numpy). + */ + +template <class T> +class ArrayHandle +{ + ArrayHandle(const ArrayHandle &) = delete; + ArrayHandle& operator=(const ArrayHandle &) = delete; +public: + ArrayHandle() {} + ~ArrayHandle() { destroy(); } + + void allocate(Py_ssize_t size); + void setData(T *d, size_t size); + + size_t size() const { return m_size; } + T *data() const { return m_data; } + operator T*() const { return m_data; } + +private: + void destroy(); + + T *m_data = nullptr; + Py_ssize_t m_size = 0; + bool m_owned = false; +}; + +/** + * Similar to ArrayHandle for fixed size 2 dimensional arrays. + * columns is the size of the last dimension + * It only has a setData() methods since it will be used for numpy only. + */ + +template <class T, int columns> +class Array2Handle +{ +public: + typedef T RowType[columns]; + + Array2Handle() {} + + operator RowType*() const { return m_rows; } + + void setData(RowType *d) { m_rows = d; } + +private: + RowType *m_rows = nullptr; +}; + +/// Returns the converter for an array type. +LIBSHIBOKEN_API SbkArrayConverter *arrayTypeConverter(int index, int dimension = 1); + +template <class T> +struct ArrayTypeIndex{ + enum : int { index = SBK_UNIMPLEMENTED_ARRAY_IDX }; +}; + +template <> struct ArrayTypeIndex<double> { enum : int { index = SBK_DOUBLE_ARRAY_IDX }; }; +template <> struct ArrayTypeIndex<float> { enum : int { index = SBK_FLOAT_ARRAY_IDX };}; +template <> struct ArrayTypeIndex<short> { enum : int { index = SBK_SHORT_ARRAY_IDX };}; +template <> struct ArrayTypeIndex<unsigned short> { enum : int { index = SBK_UNSIGNEDSHORT_ARRAY_IDX };}; +template <> struct ArrayTypeIndex<int> { enum : int { index = SBK_INT_ARRAY_IDX };}; +template <> struct ArrayTypeIndex<unsigned> { enum : int { index = SBK_UNSIGNEDINT_ARRAY_IDX };}; +template <> struct ArrayTypeIndex<long long> { enum : int { index = SBK_LONGLONG_ARRAY_IDX };}; +template <> struct ArrayTypeIndex<unsigned long long> { enum : int { index = SBK_UNSIGNEDLONGLONG_ARRAY_IDX };}; + +template<typename T> SbkArrayConverter *ArrayTypeConverter(int dimension) +{ return arrayTypeConverter(ArrayTypeIndex<T>::index, dimension); } + +// ArrayHandle methods +template<class T> +void ArrayHandle<T>::allocate(Py_ssize_t size) +{ + destroy(); + m_data = new T[size]; + m_size = size; + m_owned = true; +} + +template<class T> +void ArrayHandle<T>::setData(T *d, size_t size) +{ + destroy(); + m_data = d; + m_size = size; + m_owned = false; +} + +template<class T> +void ArrayHandle<T>::destroy() +{ + if (m_owned) + delete [] m_data; + m_data = nullptr; + m_size = 0; + m_owned = false; +} + +} // namespace Conversions +} // namespace Shiboken + +#endif // SBKARRAYCONVERTERS_H diff --git a/sources/shiboken2/libshiboken/sbkarrayconverter_p.h b/sources/shiboken2/libshiboken/sbkarrayconverter_p.h new file mode 100644 index 000000000..9384fbcf5 --- /dev/null +++ b/sources/shiboken2/libshiboken/sbkarrayconverter_p.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of PySide2. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SBKARRAYCONVERTER_P_H +#define SBKARRAYCONVERTER_P_H + +#include "sbkconverter_p.h" +#include <vector> + +extern "C" +{ + +typedef PythonToCppFunc (*IsArrayConvertibleToCppFunc)(PyObject*, int dim1, int dim2); +/** + * \internal + * Private structure of SbkArrayConverter. + */ + +struct SbkArrayConverter +{ + std::vector<IsArrayConvertibleToCppFunc> toCppConversions; +}; + +} // extern "C" + +#endif // SBKARRAYCONVERTER_P_H diff --git a/sources/shiboken2/libshiboken/sbkconverter.cpp b/sources/shiboken2/libshiboken/sbkconverter.cpp index 2a51edd76..b86e09fa2 100644 --- a/sources/shiboken2/libshiboken/sbkconverter.cpp +++ b/sources/shiboken2/libshiboken/sbkconverter.cpp @@ -39,6 +39,7 @@ #include "sbkconverter.h" #include "sbkconverter_p.h" +#include "sbkarrayconverter_p.h" #include "basewrapper_p.h" #include "autodecref.h" #include "sbkdbg.h" @@ -54,6 +55,8 @@ static ConvertersMap converters; namespace Shiboken { namespace Conversions { +void initArrayConverters(); + void init() { static SbkConverter* primitiveTypeConverters[] = { @@ -95,9 +98,11 @@ void init() converters["unsigned long"] = primitiveTypeConverters[SBK_UNSIGNEDLONG_IDX]; converters["unsigned short"] = primitiveTypeConverters[SBK_UNSIGNEDSHORT_IDX]; converters["void*"] = primitiveTypeConverters[SBK_VOIDPTR_IDX]; + + initArrayConverters(); } -static SbkConverter* createConverterObject(PyTypeObject* type, +SbkConverter *createConverterObject(PyTypeObject *type, PythonToCppFunc toCppPointerConvFunc, IsConvertibleToCppFunc toCppPointerCheckFunc, CppToPythonFunc pointerToPythonFunc, @@ -254,6 +259,17 @@ PythonToCppFunc isPythonToCppConvertible(const SbkConverter *converter, PyObject return IsPythonToCppConvertible(converter, pyIn); } +PythonToCppFunc isPythonToCppConvertible(const SbkArrayConverter *converter, + int dim1, int dim2, PyObject *pyIn) +{ + assert(pyIn); + for (IsArrayConvertibleToCppFunc f : converter->toCppConversions) { + if (PythonToCppFunc c = f(pyIn, dim1, dim2)) + return c; + } + return nullptr; +} + PythonToCppFunc isPythonToCppReferenceConvertible(const SbkObjectType *type, PyObject *pyIn) { if (pyIn != Py_None) { diff --git a/sources/shiboken2/libshiboken/sbkconverter.h b/sources/shiboken2/libshiboken/sbkconverter.h index 7489b930d..18b6bbcf3 100644 --- a/sources/shiboken2/libshiboken/sbkconverter.h +++ b/sources/shiboken2/libshiboken/sbkconverter.h @@ -67,6 +67,7 @@ extern "C" * using the functions provided by the converter API. */ struct SbkConverter; +struct SbkArrayConverter; /** * Given a void pointer to a C++ object, this function must return @@ -241,6 +242,8 @@ LIBSHIBOKEN_API PythonToCppFunc isPythonToCppReferenceConvertible(const SbkObjec /// This is the same as isPythonToCppValueConvertible function. LIBSHIBOKEN_API PythonToCppFunc isPythonToCppConvertible(const SbkConverter *converter, PyObject *pyIn); +LIBSHIBOKEN_API PythonToCppFunc isPythonToCppConvertible(const SbkArrayConverter *converter, + int dim1, int dim2, PyObject *pyIn); /** * Returns the C++ pointer for the \p pyIn object cast to the type passed via \p desiredType. diff --git a/sources/shiboken2/libshiboken/sbkconverter_p.h b/sources/shiboken2/libshiboken/sbkconverter_p.h index b38561780..fd1f7b6b2 100644 --- a/sources/shiboken2/libshiboken/sbkconverter_p.h +++ b/sources/shiboken2/libshiboken/sbkconverter_p.h @@ -571,4 +571,14 @@ struct Primitive<void*> : OnePrimitive<void*> } }; +namespace Shiboken { +namespace Conversions { +SbkConverter *createConverterObject(PyTypeObject *type, + PythonToCppFunc toCppPointerConvFunc, + IsConvertibleToCppFunc toCppPointerCheckFunc, + CppToPythonFunc pointerToPythonFunc, + CppToPythonFunc copyToPythonFunc); +} // namespace Conversions +} // namespace Shiboken + #endif // SBK_CONVERTER_P_H diff --git a/sources/shiboken2/libshiboken/sbknumpyarrayconverter.cpp b/sources/shiboken2/libshiboken/sbknumpyarrayconverter.cpp new file mode 100644 index 000000000..e941fbe26 --- /dev/null +++ b/sources/shiboken2/libshiboken/sbknumpyarrayconverter.cpp @@ -0,0 +1,308 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of PySide2. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "sbkarrayconverter.h" +#include "helper.h" +#include "sbkconverter.h" +#include "sbkconverter_p.h" +#include "sbkarrayconverter_p.h" + +#include <numpy/arrayobject.h> + +#include <algorithm> +#include <iostream> +#include <cstdint> + +enum { debugNumPy = 0 }; + +struct TypeCharMapping +{ + NPY_TYPES type; + const char *name; +}; + +static const TypeCharMapping typeCharMappings[] = { +{NPY_BYTE, "NPY_BYTE"}, +{NPY_UBYTE, "NPY_UBYTE"}, +{NPY_SHORT, "NPY_SHORT"}, +{NPY_USHORT, "NPY_USHORT"}, +{NPY_INT, "NPY_INT"}, +{NPY_UINT, "NPY_UINT"}, +{NPY_LONG, "NPY_LONG"}, +{NPY_ULONG, "NPY_ULONG"}, +{NPY_LONGLONG, "NPY_LONGLONG"}, +{NPY_ULONGLONG, "NPY_ULONGLONG"}, +{NPY_FLOAT, "NPY_FLOAT"}, +{NPY_DOUBLE, "NPY_DOUBLE"} +}; + +const char *npTypeName(npy_intp t) +{ + const TypeCharMapping *end = typeCharMappings + sizeof(typeCharMappings) / sizeof(typeCharMappings[0]); + const TypeCharMapping *result = + std::find_if(typeCharMappings, end, + [t] (const TypeCharMapping &m) { return m.type == t; }); + return result != end ? result->name : nullptr; +} + +std::ostream &operator<<(std::ostream &str, PyArrayObject *o) +{ + str << "PyArrayObject("; + if (o) { + const npy_intp npType = PyArray_TYPE(o); + if (const char *name = npTypeName(npType)) + str << name; + else + str << "type=" << npType; + const int nDim = PyArray_NDIM(o); + const npy_intp *dims = PyArray_DIMS(o); + for (int d = 0; d < nDim; ++d) + str << '[' << dims[d] << ']'; + str << ", "; + const int flags = PyArray_FLAGS(o); + if ((flags & NPY_ARRAY_C_CONTIGUOUS) != 0) + str << " NPY_ARRAY_C_CONTIGUOUS"; + if ((flags & NPY_ARRAY_F_CONTIGUOUS) != 0) + str << " NPY_ARRAY_F_CONTIGUOUS"; + if ((flags & NPY_ARRAY_OWNDATA) != 0) + str << " NPY_ARRAY_OWNDATA"; + if ((flags & NPY_ARRAY_FORCECAST) != 0) + str << " NPY_ARRAY_FORCECAST"; + if ((flags & NPY_ARRAY_ENSURECOPY) != 0) + str << " NPY_ARRAY_ENSURECOPY"; + if ((flags & NPY_ARRAY_ENSUREARRAY) != 0) + str << " NPY_ARRAY_ENSUREARRAY"; + if ((flags & NPY_ARRAY_ELEMENTSTRIDES) != 0) + str << " NPY_ARRAY_ELEMENTSTRIDES"; + if ((flags & NPY_ARRAY_ALIGNED) != 0) + str << " NPY_ARRAY_ALIGNED"; + if ((flags & NPY_ARRAY_NOTSWAPPED) != 0) + str << " NPY_ARRAY_NOTSWAPPED"; + if ((flags & NPY_ARRAY_WRITEABLE) != 0) + str << " NPY_ARRAY_WRITEABLE"; + if ((flags & NPY_ARRAY_UPDATEIFCOPY) != 0) + str << " NPY_ARRAY_UPDATEIFCOPY"; + } else { + str << '0'; + } + str << ')'; + return str; +} + +namespace Shiboken { +namespace Conversions { + +// Internals from sbkarrayconverter.cpp +SbkArrayConverter *createArrayConverter(IsArrayConvertibleToCppFunc toCppCheckFunc); +void setArrayTypeConverter(int index, int dimension, SbkArrayConverter *c); +SbkArrayConverter *unimplementedArrayConverter(); + +template <int dimension> +static bool isPrimitiveArray(PyObject *pyIn, int expectedNpType) +{ + if (!PyArray_Check(pyIn)) + return false; + PyArrayObject *pya = reinterpret_cast<PyArrayObject *>(pyIn); + if (debugNumPy) { + std::cerr << __FUNCTION__ << "(expectedNpType=" << expectedNpType; + if (const char *name = npTypeName(expectedNpType)) + std::cerr << " (" << name << ')'; + std::cerr << ' ' << pya << '\n'; + } + + const int dim = PyArray_NDIM(pya); + if (dim != dimension) { + warning(PyExc_RuntimeWarning, 0, + "%d dimensional numpy array passed to a function expecting a %d dimensional array.", + dim, dimension); + return false; + } + if ((PyArray_FLAGS(pya) & NPY_ARRAY_C_CONTIGUOUS) == 0) { + warning(PyExc_RuntimeWarning, 0, + "Cannot handle numpy arrays that do not have NPY_ARRAY_C_CONTIGUOUS set."); + return false; + } + const int actualNpType = PyArray_TYPE(pya); + if (actualNpType != expectedNpType) { + const char *actualName = npTypeName(actualNpType); + const char *expectedName = npTypeName(expectedNpType); + warning(PyExc_RuntimeWarning, 0, + "A numpy array of type %d (%s) was passed to a function expecting type %d (%s).", + actualNpType, actualName ? actualName : "", + expectedNpType, expectedName ? expectedName : ""); + return false; + } + return true; +} + +static inline bool primitiveArrayCheck1(PyObject *pyIn, int expectedNpType, int expectedSize) +{ + if (!isPrimitiveArray<1>(pyIn, expectedNpType)) + return false; + if (expectedSize >= 0) { + PyArrayObject *pya = reinterpret_cast<PyArrayObject *>(pyIn); + const int size = int(PyArray_DIMS(pya)[0]); + if (size < expectedSize) { + warning(PyExc_RuntimeWarning, 0, "A numpy array of size %d was passed to a function expects %d.", + size, expectedSize); + return false; + } + } + return true; +} + +// Convert one-dimensional array +template <class T> +static void convertArray1(PyObject *pyIn, void *cppOut) +{ + ArrayHandle<T> *handle = reinterpret_cast<ArrayHandle<T> *>(cppOut); + PyArrayObject *pya = reinterpret_cast<PyArrayObject *>(pyIn); + const npy_intp size = PyArray_DIMS(pya)[0]; + if (debugNumPy) + std::cerr << __FUNCTION__ << ' ' << size << '\n'; + handle->setData(reinterpret_cast<T *>(PyArray_DATA(pya)), size_t(size)); +} + +// Convert 2 dimensional array +template <class T> +static void convertArray2(PyObject *pyIn, void *cppOut) +{ + typedef typename Array2Handle<T, 1>::RowType RowType; + Array2Handle<T, 1> *handle = reinterpret_cast<Array2Handle<T, 1> *>(cppOut); + PyArrayObject *pya = reinterpret_cast<PyArrayObject *>(pyIn); + handle->setData(reinterpret_cast<RowType *>(PyArray_DATA(pya))); +} + +template <class T, int NumPyType> +static PythonToCppFunc checkArray1(PyObject *pyIn, int dim1, int /* dim2 */) +{ + return primitiveArrayCheck1(pyIn, NumPyType, dim1) ? convertArray1<T> : nullptr; +} + +static inline bool primitiveArrayCheck2(PyObject *pyIn, int expectedNpType, int expectedDim1, int expectedDim2) +{ + if (!isPrimitiveArray<2>(pyIn, expectedNpType)) + return false; + if (expectedDim2 >= 0) { + PyArrayObject *pya = reinterpret_cast<PyArrayObject *>(pyIn); + const int dim1 = int(PyArray_DIMS(pya)[0]); + const int dim2 = int(PyArray_DIMS(pya)[1]); + if (dim1 != expectedDim1 || dim2 != expectedDim2) { + warning(PyExc_RuntimeWarning, 0, "A numpy array of size %dx%d was passed to a function that expects %dx%d.", + dim1, dim2, expectedDim1, expectedDim2); + return false; + } + } + return true; +} + +template <class T, int NumPyType> +static PythonToCppFunc checkArray2(PyObject *pyIn, int dim1, int dim2) +{ + return primitiveArrayCheck2(pyIn, NumPyType, dim1, dim2) ? convertArray2<T> : nullptr; +} + +template <class T> +static void setOrExtendArrayConverter(int dimension, IsArrayConvertibleToCppFunc toCppCheckFunc) +{ + SbkArrayConverter *arrayConverter = ArrayTypeConverter<T>(dimension); + if (arrayConverter == unimplementedArrayConverter()) { + arrayConverter = createArrayConverter(toCppCheckFunc); + setArrayTypeConverter(ArrayTypeIndex<T>::index, dimension, arrayConverter); + } else { + arrayConverter->toCppConversions.push_back(toCppCheckFunc); + } +} + +// Extend the converters for primitive type one-dimensional arrays by NumPy ones. +template <class T, int NumPyType> +static inline void extendArrayConverter1() +{ + setOrExtendArrayConverter<T>(1, checkArray1<T, NumPyType>); +} + +// Extend the converters for primitive type one-dimensional arrays by NumPy ones. +template <class T, int NumPyType> +static inline void extendArrayConverter2() +{ + setOrExtendArrayConverter<T>(2, checkArray2<T, NumPyType>); +} + +void initNumPyArrayConverters() +{ + // Expanded from macro "import_array" in __multiarray_api.h + // Make sure to read about the magic defines PY_ARRAY_UNIQUE_SYMBOL etc., + // when changing this or spreading the code over several source files. + if (_import_array() < 0) { + PyErr_Print(); + PyErr_SetString(PyExc_ImportError, "numpy.core.multiarray failed to import"); + return; + } + + // Extend the converters for primitive types by NumPy ones. + extendArrayConverter1<short, NPY_SHORT>(); + extendArrayConverter2<short, NPY_SHORT>(); + extendArrayConverter1<unsigned short, NPY_SHORT>(); + extendArrayConverter2<unsigned short, NPY_SHORT>(); + extendArrayConverter1<int, NPY_INT>(); + extendArrayConverter2<int, NPY_INT>(); + extendArrayConverter1<unsigned int, NPY_UINT>(); + extendArrayConverter2<unsigned int, NPY_UINT>(); + extendArrayConverter1<long long, NPY_LONGLONG>(); + extendArrayConverter2<long long, NPY_LONGLONG>(); + extendArrayConverter1<unsigned long long, NPY_ULONGLONG>(); + if (sizeof(long) == 8) { // UNIX/LP64: ints typically come as long + extendArrayConverter1<long long, NPY_LONG>(); + extendArrayConverter2<long long, NPY_LONG>(); + extendArrayConverter1<unsigned long long, NPY_ULONG>(); + extendArrayConverter2<unsigned long long, NPY_ULONG>(); + } else if (sizeof(long) == sizeof(int)) { + extendArrayConverter1<int, NPY_LONG>(); + extendArrayConverter1<unsigned, NPY_ULONG>(); + extendArrayConverter2<int, NPY_LONG>(); + extendArrayConverter2<unsigned, NPY_ULONG>(); + } + extendArrayConverter1<float, NPY_FLOAT>(); + extendArrayConverter2<float, NPY_FLOAT>(); + extendArrayConverter1<double, NPY_DOUBLE>(); + extendArrayConverter2<double, NPY_DOUBLE>(); +} + +} // namespace Conversions +} // namespace Shiboken diff --git a/sources/shiboken2/libshiboken/shiboken.h b/sources/shiboken2/libshiboken/shiboken.h index 2738bf51f..5bdef1b86 100644 --- a/sources/shiboken2/libshiboken/shiboken.h +++ b/sources/shiboken2/libshiboken/shiboken.h @@ -48,6 +48,7 @@ #include "gilstate.h" #include "threadstatesaver.h" #include "helper.h" +#include "sbkarrayconverter.h" #include "sbkconverter.h" #include "sbkenum.h" #include "sbkmodule.h" diff --git a/sources/shiboken2/tests/libsample/functions.cpp b/sources/shiboken2/tests/libsample/functions.cpp index 4a15cdae8..bf73d5ed7 100644 --- a/sources/shiboken2/tests/libsample/functions.cpp +++ b/sources/shiboken2/tests/libsample/functions.cpp @@ -28,7 +28,9 @@ #include "functions.h" #include <string.h> +#include <algorithm> #include <iostream> +#include <numeric> using namespace std; @@ -197,6 +199,45 @@ acceptOddBoolReference(OddBool& x) return x; } +int sumIntArray(int array[4]) +{ + return std::accumulate(array, array + 4, 0); +} + +double sumDoubleArray(double array[4]) +{ + return std::accumulate(array, array + 4, double(0)); +} + +int sumIntMatrix(int m[2][3]) +{ + int result = 0; + for (int r = 0; r < 2; ++r) { + for (int c = 0; c < 3; ++c) + result += m[r][c]; + } + return result; +} + +double sumDoubleMatrix(double m[2][3]) +{ + double result = 0; + for (int r = 0; r < 2; ++r) { + for (int c = 0; c < 3; ++c) + result += m[r][c]; + } + return result; +} + +ArrayModifyTest::ArrayModifyTest() +{ +} + +int ArrayModifyTest::sumIntArray(int n, int *array) +{ + return std::accumulate(array, array + n, 0); +} + ClassWithFunctionPointer::ClassWithFunctionPointer() { callFunctionPointer(0, &ClassWithFunctionPointer::doNothing); diff --git a/sources/shiboken2/tests/libsample/functions.h b/sources/shiboken2/tests/libsample/functions.h index 89a175bc4..a53f97c6e 100644 --- a/sources/shiboken2/tests/libsample/functions.h +++ b/sources/shiboken2/tests/libsample/functions.h @@ -81,6 +81,17 @@ LIBSAMPLE_API double acceptDouble(double x); LIBSAMPLE_API int acceptIntReference(int& x); LIBSAMPLE_API OddBool acceptOddBoolReference(OddBool& x); +LIBSAMPLE_API int sumIntArray(int array[4]); +LIBSAMPLE_API double sumDoubleArray(double array[4]); +LIBSAMPLE_API int sumIntMatrix(int m[2][3]); +LIBSAMPLE_API double sumDoubleMatrix(double m[2][3]); + +class LIBSAMPLE_API ArrayModifyTest +{ +public: + ArrayModifyTest(); + int sumIntArray(int n, int *array); +}; class LIBSAMPLE_API ClassWithFunctionPointer { diff --git a/sources/shiboken2/tests/samplebinding/CMakeLists.txt b/sources/shiboken2/tests/samplebinding/CMakeLists.txt index 1b97bd0e8..b3c9df0dd 100644 --- a/sources/shiboken2/tests/samplebinding/CMakeLists.txt +++ b/sources/shiboken2/tests/samplebinding/CMakeLists.txt @@ -7,6 +7,7 @@ ${CMAKE_CURRENT_SOURCE_DIR}/typesystem_sample.xml set(sample_SRC ${CMAKE_CURRENT_BINARY_DIR}/sample/abstractmodifications_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/abstract_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/arraymodifytest_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/base1_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/base2_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/base3_wrapper.cpp diff --git a/sources/shiboken2/tests/samplebinding/array_numpy_test.py b/sources/shiboken2/tests/samplebinding/array_numpy_test.py new file mode 100644 index 000000000..bde46f2e3 --- /dev/null +++ b/sources/shiboken2/tests/samplebinding/array_numpy_test.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python + +############################################################################# +## +## Copyright (C) 2017 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the test suite of PySide2. +## +## $QT_BEGIN_LICENSE:GPL-EXCEPT$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 3 as published by the Free Software +## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +'''Test case for NumPy Array types.''' + +import unittest +import sample + +hasNumPy = False + +try: + import numpy + hasNumPy = True +except ImportError: + pass + +class ArrayTester(unittest.TestCase): + '''Test case for NumPy arrays.''' + + def testIntArray(self): + intList = numpy.array([1, 2, 3, 4], dtype = 'int32') + self.assertEqual(sample.sumIntArray(intList), 10) + + def testDoubleArray(self): + doubleList = numpy.array([1, 2, 3, 4], dtype = 'double') + self.assertEqual(sample.sumDoubleArray(doubleList), 10) + + def testIntMatrix(self): + intMatrix = numpy.array([[1, 2, 3], [4, 5, 6]], dtype = 'int32') + self.assertEqual(sample.sumIntMatrix(intMatrix), 21) + + def testDoubleMatrix(self): + doubleMatrix = numpy.array([[1, 2, 3], [4, 5, 6]], dtype = 'double') + self.assertEqual(sample.sumDoubleMatrix(doubleMatrix), 21) + +if __name__ == '__main__' and hasNumPy: + unittest.main() diff --git a/sources/shiboken2/tests/samplebinding/array_sequence_test.py b/sources/shiboken2/tests/samplebinding/array_sequence_test.py new file mode 100644 index 000000000..0fd2ec636 --- /dev/null +++ b/sources/shiboken2/tests/samplebinding/array_sequence_test.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python + +############################################################################# +## +## Copyright (C) 2017 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the test suite of PySide2. +## +## $QT_BEGIN_LICENSE:GPL-EXCEPT$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 3 as published by the Free Software +## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +'''Test case for Array types (PySequence).''' + +import unittest +import sample + +class ArrayTester(unittest.TestCase): + '''Test case for arrays.''' + + def testIntArray(self): + intList = [1, 2, 3, 4] + self.assertEqual(sample.sumIntArray(intList), 10) + + def testIntArrayModified(self): + intList = [1, 2, 3, 4] + tester = sample.ArrayModifyTest() + self.assertEqual(tester.sumIntArray(4, intList), 10) + + def testDoubleArray(self): + doubleList = [1.2, 2.3, 3.4, 4.5] + self.assertEqual(sample.sumDoubleArray(doubleList), 11.4) + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken2/tests/samplebinding/typesystem_sample.xml b/sources/shiboken2/tests/samplebinding/typesystem_sample.xml index 089f835fc..efc4205ae 100644 --- a/sources/shiboken2/tests/samplebinding/typesystem_sample.xml +++ b/sources/shiboken2/tests/samplebinding/typesystem_sample.xml @@ -501,11 +501,21 @@ <function signature="gimmeInt()" /> <function signature="gimmeDouble()" /> <function signature="makeCString()" /> + <function signature="sumIntArray(int[4])"/> + <function signature="sumDoubleArray(double[4])"/> + <function signature="sumIntMatrix(int[2][3])"/> + <function signature="sumDoubleMatrix(double[2][3])"/> <function signature="multiplyPair(std::pair<double, double>)" /> <function signature="returnCString()" /> <function signature="overloadedFunc(double)" /> <function signature="overloadedFunc(int)" /> + <value-type name="ArrayModifyTest"> + <modify-function signature="sumIntArray(int, int*)"> + <modify-argument index="2"><array/></modify-argument> + </modify-function> + </value-type> + <value-type name="ClassWithFunctionPointer"> <suppress-warning text="skipping function 'ClassWithFunctionPointer::callFunctionPointer', unmatched parameter type 'void (*)(void*)'" /> </value-type> @@ -1159,7 +1169,7 @@ <!-- change the name of this virtual method --> <modify-function signature="className()" rename="name"/> - <modify-function signature="sumPointArray(int, const Point*)"> + <modify-function signature="sumPointArray(int, const Point[])"> <modify-argument index="1"> <remove-argument/> <conversion-rule class="native"> @@ -1950,7 +1960,7 @@ <define-ownership owner="c++"/> </modify-argument> </modify-function> - <modify-function signature="acceptSequence(const char**)"> + <modify-function signature="acceptSequence(const char*[])"> <modify-argument index="1"> <replace-type modified-type="PySequence" /> <conversion-rule class="native"> |