diff options
239 files changed, 12079 insertions, 8109 deletions
diff --git a/.gitignore b/.gitignore index 6a8b69288..903fc81f3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ /build /dist /pyside*_build -/pyside*_package /pyside*_install /PySide /PySide-*.*.* @@ -13,3 +12,5 @@ distribute-*.egg distribute-*.tar.gz explore2 build_history/2* +*.qdocconf +*.qdocconf.in diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index dd79b2380..000000000 --- a/MANIFEST.in +++ /dev/null @@ -1,40 +0,0 @@ -# -# MANIFEST.in -# -# Manifest template for creating the PySide source distribution. - -include MANIFEST.in -include CHANGES.rst -include README.rst -include ez_setup.py -include setup.py -include popenasync.py -include qtinfo.py -include utils.py - -# sources -recursive-include sources/patchelf ** -recursive-include sources/shiboken2 ** -recursive-include sources/pyside2 ** -recursive-include sources/pyside2-tools ** -# ignore .git -recursive-exclude sources/shiboken2/.git ** -recursive-exclude sources/pyside2/.git ** -recursive-exclude sources/pyside2-tools/.git ** - -# PySide package -recursive-include pyside_package/PySide2 ** -recursive-include pyside_package/PySide2/docs ** -recursive-include pyside_package/PySide2/plugins ** -recursive-include pyside_package/PySide2s ** -recursive-include pyside_package/PySide2/translations ** -recursive-include pyside_package/PySide2include ** -recursive-include pyside_package/PySide2/typesystems ** -recursive-include pyside_package/PySide2/examples ** - -# pysideuic package -recursive-include pyside_package/pysideuic ** -recursive-include pyside_package/pysideuic/Compiler ** -recursive-include pyside_package/pysideuic/port_v2 ** -recursive-include pyside_package/pysideuic/port_v3 ** -recursive-include pyside_package/pysideuic/widget-plugins ** @@ -1,53 +1,9 @@ -# PySide2 +# Qt For Python -### Introduction - -PySide is the [Python Qt bindings project](http://wiki.qt.io/PySide2), providing +Qt For Python is the [Python Qt bindings project](http://wiki.qt.io/PySide2), providing access to the complete Qt 5.x framework as well as to generator tools for rapidly generating bindings for any C++ libraries. -The PySide project is developed in the open, with all facilities you'd expect -from any modern OSS project such as all code in a git repository and an open -design process. We welcome any contribution conforming to the -[Qt Contribution Agreement](https://www.qt.io/contributionagreement/). - - -PySide 2 supports Qt5. For building, please read about -[getting started](https://wiki.qt.io/PySide2_GettingStarted). -Then download the sources by running - - git clone https://code.qt.io/pyside/pyside-setup - -### Building - -#### Dependencies - -PySide versions following 5.6 use a C++ parser based on -[Clang](http://clang.org/). The Clang library (C-bindings), version 3.9 or -higher is required for building. Prebuilt versions of it can be downloaded from -[download.qt.io](http://download.qt.io/development_releases/prebuilt/libclang/). - -After unpacking the archive, set the environment variable *LLVM_INSTALL_DIR* to -point to the folder containing the *include* and *lib* directories of Clang: - - 7z x .../libclang-release_39-linux-Rhel7.2-gcc5.3-x86_64.7z - export LLVM_INSTALL_DIR=$PWD/libclang - -On Windows: - - 7z x .../libclang-release_39-windows-vs2015_64.7z - SET LLVM_INSTALL_DIR=%CD%\libclang - -#### Build Instructions - -You might consider using a virtual environment as described at -[getting started](https://wiki.qt.io/PySide2_GettingStarted). -You should be able to build: - - cd pyside-setup - python setup.py install +shiboken2 is the generator used to build the bindings. -The setup script will try to find the location of the qmake tool of the Qt -version to be used and the cmake build tool in the path. Non-standard -locations can be specified by the *--qmake=path_to_qmake* or -*--cmake=path_to_cmake* command line options. +See README.pyside2.md and README.shiboken2.md for details. diff --git a/README.pyside2.md b/README.pyside2.md new file mode 100644 index 000000000..53f7bc9d0 --- /dev/null +++ b/README.pyside2.md @@ -0,0 +1,71 @@ +# PySide2 + +### Introduction + +PySide is the [Python Qt bindings project](http://wiki.qt.io/Qt_for_Python), +providing access to the complete Qt 5.12+ framework as well as to generator +tools for rapidly generating Python bindings for any C++ libraries. + +The PySide project is developed in the open, with all facilities you'd expect +from any modern OSS project such as all code in a git repository and an open +design process. We welcome any contribution conforming to the +[Qt Contribution Agreement](https://www.qt.io/contributionagreement/). + +### Installation + +Since the release of the [Technical Preview](https://blog.qt.io/blog/2018/06/13/qt-python-5-11-released/) +it is possible to install via `pip`, both from Qt's servers +and [PyPi](https://pypi.org/project/PySide2/): + + pip install PySide2 + +#### Dependencies + +PySide versions following 5.12 use a C++ parser based on +[Clang](http://clang.org/). The Clang library (C-bindings), version 6.0 or +higher is required for building. Prebuilt versions of it can be downloaded from +[download.qt.io](http://download.qt.io/development_releases/prebuilt/libclang/). + +After unpacking the archive, set the environment variable *LLVM_INSTALL_DIR* to +point to the folder containing the *include* and *lib* directories of Clang: + + 7z x .../libclang-release_60-linux-Rhel7.2-gcc5.3-x86_64-clazy.7z + export LLVM_INSTALL_DIR=$PWD/libclang + +On Windows: + + 7z x .../libclang-release_60-windows-vs2015_64-clazy.7z + SET LLVM_INSTALL_DIR=%CD%\libclang + +### Building from source + +For building PySide2 from scratch, please read about +[getting started](https://wiki.qt.io/Qt_for_Python/GettingStarted). +This process will include getting the code: + + git clone https://code.qt.io/pyside/pyside-setup + cd pyside-setup + git branch --track 5.12 origin/5.12 + git checkout 5.12 + +then install the dependencies, and following the instructions per platform. +A common build command will look like: + + python setup.py install --qmake=<path/to/qmake/> --jobs=8 --build-tests + +You can obtain more information about the options to build PySide +and Shiboken in [our wiki](https://wiki.qt.io/Qt_for_Python/). + +### Documentation and Bugs + +You can find more information about the PySide2 module API in the +[official Qt for Python documentation](https://doc.qt.io/qtforpython/). + +If you come across any issue, please file a bug report at our +[JIRA tracker](https://bugreports.qt.io/projects/PYSIDE) following +our [guidelines](https://wiki.qt.io/Qt_for_Python/Reporting_Bugs). + +### Community + +Check *#qt-pyside*, our official IRC channel on FreeNode, +or contact us via our [mailing list](http://lists.qt-project.org/mailman/listinfo/pyside). diff --git a/README.shiboken2-generator.md b/README.shiboken2-generator.md new file mode 100644 index 000000000..f29f40634 --- /dev/null +++ b/README.shiboken2-generator.md @@ -0,0 +1,37 @@ +# shiboken2-generator + +Shiboken is the generator used by the Qt for Python project. +It outputs C++ code for CPython extensions, which can be compiled +and transformed into a Python module. + +C++ projects based on Qt can be wrapped, but also projects +which are not related to Qt. + +## How does it work? + +Shiboken uses an API Extractor that does most of the job, +but it requires a typesystem (XML file) to customize how the +C++ classes/methods will be exposed to Python. + +The typesystem allows you to remove arguments from signatures, +modify return types, inject code and add conversion rules +from the C++ data types to Python data types, manipulate +the ownership of the objects, etc. + +# Examples + +An example related to wrap a C++ library not depending on Qt +can be found in our [repository](https://code.qt.io/cgit/pyside/pyside-setup.git/tree/examples/samplebinding). + +Additionally, you can find a couple of tests inside the +[git repository](https://code.qt.io/cgit/pyside/pyside-setup.git/tree/sources/shiboken2/tests). + +For a more advanced case regarding extending a Qt/C++ application +with Python bindings based on the idea of the PySide module, +you can check the [scriptableapplication](https://code.qt.io/cgit/pyside/pyside-setup.git/tree/examples/scriptableapplication) +example in our repository. + +# Documentation + +You can find more information about Shiboken in our +[official documentation page](https://doc.qt.io/qtforpython/shiboken2/). diff --git a/README.shiboken2.md b/README.shiboken2.md new file mode 100644 index 000000000..d9cd32a40 --- /dev/null +++ b/README.shiboken2.md @@ -0,0 +1,13 @@ +# shiboken2 module + +The purpose of the shiboken2 Python module is to access information +related to the binding generation that could be used to integrate +C++ programs to Python, or even to get useful information to debug +an application. + +Mostly the idea is to interact with Shiboken objects, +where one can check if it is valid, or if the generated Python wrapper +is invalid after the underlying C++ object has been destroyed. + +More information on the available functions can be found +in our [official documentation](https://doc.qt.io/qtforpython/shiboken2/shibokenmodule.html) diff --git a/build_scripts/build_scripts.pyqtc b/build_scripts/build_scripts.pyqtc new file mode 100644 index 000000000..1fc1c9664 --- /dev/null +++ b/build_scripts/build_scripts.pyqtc @@ -0,0 +1,18 @@ +__init__.py +config.py +main.py +options.py +platforms +qtinfo.py +setup_runner.py +utils.py +wheel_override.py +platforms/__init__.py +platforms/linux.py +platforms/macos.py +platforms/unix.py +platforms/windows_desktop.py +../setup.py +../coin_build_instructions.py +../coin_test_instructions.py + diff --git a/build_scripts/config.py b/build_scripts/config.py new file mode 100644 index 000000000..78d7d4040 --- /dev/null +++ b/build_scripts/config.py @@ -0,0 +1,393 @@ +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of Qt for Python. +## +## $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$ +## +############################################################################# + +import sys, os +import distutils.log as log + + +class Config(object): + def __init__(self): + # Constants + self._build_type_all = "all" + self._invocation_type_top_level = "top-level" + self._invocation_type_internal = "internal" + + # The keyword arguments which will be given to setuptools.setup + self.setup_kwargs = {} + + # The setup.py invocation type. + # top-level + # internal + self.invocation_type = None + + # The type of the top-level build. + # all - build shiboken2 module, shiboken2-generator and PySide2 + # modules + # shiboken2 - build only shiboken2 module + # shiboken2-generator - build only the shiboken2-generator + # pyside2 - build only PySide2 modules + self.build_type = None + + # The internal build type, used for internal invocations of + # setup.py to build a specific module only. + self.internal_build_type = None + + # Options that can be given to --build-type and + # --internal-build-type + self.shiboken_module_option_name = "shiboken2" + self.shiboken_generator_option_name = "shiboken2-generator" + self.pyside_option_name = "pyside2" + + # Names to be passed to setuptools.setup() name key, + # so not package name, but rather project name as it appears + # in the wheel name and on PyPi. + self.shiboken_module_st_name = "shiboken2" + self.shiboken_generator_st_name = "shiboken2-generator" + self.pyside_st_name = "PySide2" + + # Used by check_allowed_python_version to validate the + # interpreter version. + self.python_version_classifiers = [ + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + ] + + self.setup_script_dir = None + + def init_config(self, build_type=None, internal_build_type=None, + cmd_class_dict=None, package_version=None, + ext_modules=None, setup_script_dir=None): + """ + Sets up the global singleton config which is used in many parts + of the setup process. + """ + + # if --internal-build-type was passed, it means that this is a + # sub-invocation to build a specific package. + if internal_build_type: + self.set_is_internal_invocation() + self.set_internal_build_type(internal_build_type) + else: + self.set_is_top_level_invocation() + + # --build-type was specified explicitly, so set it. Otherwise + # default to all. + if build_type: + self.build_type = build_type + else: + self.build_type = self._build_type_all + + self.setup_script_dir = setup_script_dir + + setup_kwargs = {} + setup_kwargs['long_description'] = self.get_long_description() + setup_kwargs['long_description_content_type'] = 'text/markdown', + setup_kwargs['keywords'] = 'Qt' + setup_kwargs['author'] = 'Qt for Python Team' + setup_kwargs['author_email'] = 'pyside@qt-project.org' + setup_kwargs['url'] = 'https://www.pyside.org' + setup_kwargs['download_url'] = 'https://download.qt.io/official_releases/QtForPython' + setup_kwargs['license'] = 'LGPL' + setup_kwargs['zip_safe'] = False + setup_kwargs['cmdclass'] = cmd_class_dict + setup_kwargs['version'] = package_version + + # Setting these two keys is still a bit of a discussion point. + # In general not setting them will allow using "build" and + # "bdist_wheel" just fine. What they do, is they specify to the + # setuptools.command.build_py command that certain pure python + # modules (.py files) exist in the specified package location, + # and that they should be copied over to the setuptools build + # dir. + # But it doesn't really make sense for us, because we copy all + # the necessary files to the build dir via prepare_packages() + # function anyway. + # If we don't set them, the build_py sub-command will be + # skipped, but the build command will still be executed, which + # is where we run cmake / make. + # The only plausible usage of it, is if we will implement a + # correctly functioning setup.py develop command (or bdist_egg). + # But currently that doesn't seem to work. + setup_kwargs['packages'] = self.get_setup_tools_packages_for_current_build() + setup_kwargs['package_dir'] = self.get_package_name_to_dir_path_mapping() + + # Add a bogus extension module (will never be built here since + # we are overriding the build command to do it using cmake) so + # things like bdist_egg will know that there are extension + # modules and will name the dist with the full platform info. + setup_kwargs['ext_modules'] = ext_modules + + common_classifiers = [ + 'Development Status :: 5 - Production/Stable', + 'Environment :: Console', + 'Environment :: MacOS X', + 'Environment :: X11 Applications :: Qt', + 'Environment :: Win32 (MS Windows)', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)', + 'Operating System :: MacOS :: MacOS X', + 'Operating System :: POSIX', + 'Operating System :: POSIX :: Linux', + 'Operating System :: Microsoft', + 'Operating System :: Microsoft :: Windows', + 'Programming Language :: C++'] + common_classifiers.extend(self.python_version_classifiers) + common_classifiers.extend([ + 'Topic :: Database', + 'Topic :: Software Development', + 'Topic :: Software Development :: Code Generators', + 'Topic :: Software Development :: Libraries :: Application Frameworks', + 'Topic :: Software Development :: User Interfaces', + 'Topic :: Software Development :: Widget Sets']) + setup_kwargs['classifiers'] = common_classifiers + + if self.internal_build_type == self.shiboken_module_option_name: + setup_kwargs['name'] = self.shiboken_module_st_name + setup_kwargs['description'] = "Python / C++ bindings helper module", + setup_kwargs['entry_points'] = {} + + elif self.internal_build_type == self.shiboken_generator_option_name: + setup_kwargs['name'] = self.shiboken_generator_st_name + setup_kwargs['description'] = "Python / C++ bindings generator", + setup_kwargs['install_requires'] = [self.shiboken_module_st_name] + setup_kwargs['entry_points'] = { + 'console_scripts': [ + 'shiboken2 = {}.scripts.shiboken_tool:main'.format(self.package_name()), + ] + } + + elif self.internal_build_type == self.pyside_option_name: + setup_kwargs['name'] = self.pyside_st_name + setup_kwargs['description'] = ("Python bindings for the Qt cross-platform application" + " and UI framework"), + setup_kwargs['install_requires'] = [self.shiboken_module_st_name] + setup_kwargs['entry_points'] = { + 'console_scripts': [ + 'pyside2-uic = {}.scripts.uic:main'.format(self.package_name()), + 'pyside2-rcc = {}.scripts.pyside_tool:main'.format(self.package_name()), + 'pyside2-lupdate = {}.scripts.pyside_tool:main'.format(self.package_name()), + ] + } + self.setup_kwargs = setup_kwargs + + def get_long_description(self): + readme_filename = 'README.md' + changes_filename = 'CHANGES.rst' + + if self.is_internal_shiboken_module_build(): + readme_filename = 'README.shiboken2.md' + elif self.is_internal_shiboken_generator_build(): + readme_filename = 'README.shiboken2-generator.md' + elif self.is_internal_pyside_build(): + readme_filename = 'README.pyside2.md' + + content = '' + changes = '' + try: + with open(os.path.join(self.setup_script_dir, readme_filename)) as f: + readme = f.read() + except Exception as e: + log.error("Couldn't read contents of {}.".format(readme_filename)) + raise + + # Don't include CHANGES.rst for now, because we have not decided + # how to handle change files yet. + include_changes = False + if include_changes: + try: + with open(os.path.join(self.setup_script_dir, changes_filename)) as f: + changes = f.read() + except Exception as e: + log.error("Couldn't read contents of {}".format(changes_filename)) + raise + content += readme + + if changes: + content += "\n\n" + changes + + return content + + def package_name(self): + """ + Returns package name as it appears in Python's site-packages + directory. + + Package names can only be delimited by underscores, and not by + dashes. + """ + if self.is_internal_shiboken_module_build(): + return "shiboken2" + elif self.is_internal_shiboken_generator_build(): + return "shiboken2_generator" + elif self.is_internal_pyside_build(): + return "PySide2" + else: + return None + + def get_setup_tools_packages_for_current_build(self): + """ + Returns a list of packages for setup tools to consider in the + build_py command, so that it can copy the pure python files. + Not really necessary because it's done in prepare_packages() + anyway. + + This is really just to satisfy some checks in setuptools + build_py command, and if we ever properly implement the develop + command. + """ + if self.internal_build_type == self.pyside_option_name: + return [ + config.package_name(), + 'pyside2uic', + 'pyside2uic.Compiler', + 'pyside2uic.port_v{}'.format(sys.version_info[0]) + ] + elif self.internal_build_type == self.shiboken_module_option_name: + return [self.package_name()] + else: + return [] + + def get_package_name_to_dir_path_mapping(self): + """ + Used in setuptools.setup 'package_dir' argument to specify where + the actual module packages are located. + + For example when building the shiboken module, setuptools will + expect to find the "shiboken2" module sources under + "sources/shiboken2/shibokenmodule". + + This is really just to satisfy some checks in setuptools + build_py command, and if we ever properly implement the develop + command. + """ + if self.is_internal_shiboken_module_build(): + return { + self.package_name(): "sources/shiboken2/shibokenmodule" + } + elif self.is_internal_shiboken_generator_build(): + # This is left empty on purpose, because the shiboken + # generator doesn't have a python module for now. + return {} + elif self.is_internal_pyside_build(): + return { + self.package_name(): "sources/pyside2/PySide2", + "pyside2uic": "sources/pyside2-tools/pyside2uic" + } + else: + return {} + + def get_buildable_extensions(self): + """ + Used by PysideBuild.run to build the CMake projects. + :return: A list of directory names under the sources directory. + """ + if self.is_internal_shiboken_module_build() or self.is_internal_shiboken_generator_build(): + return ['shiboken2'] + elif self.is_internal_pyside_build(): + return ['pyside2', 'pyside2-tools'] + return None + + def set_is_top_level_invocation(self): + self.invocation_type = self._invocation_type_top_level + + def set_is_internal_invocation(self): + self.invocation_type = self._invocation_type_internal + + def is_top_level_invocation(self): + return self.invocation_type == self._invocation_type_top_level + + def is_internal_invocation(self): + return self.invocation_type == self._invocation_type_internal + + def is_top_level_build_all(self): + return self.build_type == self._build_type_all + + def is_top_level_build_shiboken_module(self): + return self.build_type == self.shiboken_module_option_name + + def is_top_level_build_shiboken_generator(self): + return self.build_type == self.shiboken_generator_option_name + + def is_top_level_build_pyside(self): + return self.build_type == self.pyside_option_name + + def set_internal_build_type(self, internal_build_type): + self.internal_build_type = internal_build_type + + def is_internal_shiboken_module_build(self): + return self.internal_build_type == self.shiboken_module_option_name + + def is_internal_shiboken_generator_build(self): + return self.internal_build_type == self.shiboken_generator_option_name + + def is_internal_pyside_build(self): + return self.internal_build_type == self.pyside_option_name + + def is_internal_shiboken_generator_build_and_part_of_top_level_all(self): + """ + Used to skip certain build rules and output, when we know that + the CMake build of shiboken was already done as part of the + top-level "all" build when shiboken2-module was built. + """ + return self.is_internal_shiboken_generator_build() and self.is_top_level_build_all() + + def get_allowed_top_level_build_values(self): + return [ + self._build_type_all, + self.shiboken_module_option_name, + self.shiboken_generator_option_name, + self.pyside_option_name + ] + + def get_allowed_internal_build_values(self): + return [ + self.shiboken_module_option_name, + self.shiboken_generator_option_name, + self.pyside_option_name + ] + + +config = Config() diff --git a/build_scripts/main.py b/build_scripts/main.py index b64d6f1a9..50f751caa 100644 --- a/build_scripts/main.py +++ b/build_scripts/main.py @@ -42,6 +42,7 @@ from distutils.version import LooseVersion import os import time +from .config import config from .utils import memoize, get_python_dict from .options import * @@ -90,15 +91,11 @@ def get_setuptools_extension_modules(): extension_modules = [Extension(*extension_args, **extension_kwargs)] return extension_modules -# Buildable extensions. -contained_modules = ['shiboken2', 'pyside2', 'pyside2-tools'] # Git submodules: ["submodule_name", # "location_relative_to_sources_folder"] submodules = [["pyside2-tools"]] -pyside_package_dir_name = "pyside_package" - try: import setuptools except ImportError: @@ -108,13 +105,8 @@ except ImportError: import sys import platform import re -import fnmatch - -import difflib # for a close match of dirname and module -import functools -from distutils import log -from distutils.errors import DistutilsOptionError +import distutils.log as log from distutils.errors import DistutilsSetupError from distutils.sysconfig import get_config_var from distutils.sysconfig import get_python_lib @@ -123,7 +115,7 @@ from distutils.command.build import build as _build from distutils.command.build_ext import build_ext as _build_ext from distutils.util import get_platform -from setuptools import setup, Extension +from setuptools import Extension from setuptools.command.install import install as _install from setuptools.command.install_lib import install_lib as _install_lib from setuptools.command.bdist_egg import bdist_egg as _bdist_egg @@ -132,39 +124,40 @@ from setuptools.command.build_py import build_py as _build_py from .qtinfo import QtInfo from .utils import rmtree, detect_clang, copyfile, copydir, run_process_output, run_process -from .utils import update_env_path, init_msvc_env, filter_match, macos_fix_rpaths_for_library +from .utils import update_env_path, init_msvc_env, filter_match +from .utils import macos_fix_rpaths_for_library +from .utils import linux_fix_rpaths_for_library from .platforms.unix import prepare_packages_posix from .platforms.windows_desktop import prepare_packages_win32 from .wheel_override import wheel_module_exists, get_bdist_wheel_override from textwrap import dedent -# make sure that setup.py is run with an allowed python version + def check_allowed_python_version(): + """ + Make sure that setup.py is run with an allowed python version. + """ + import re - pattern = "'Programming Language :: Python :: (\d+)\.(\d+)'" + pattern = "Programming Language :: Python :: (\d+)\.(\d+)" supported = [] - with open(setup_py_path) as setup: - for line in setup.readlines(): - found = re.search(pattern, line) - if found: - major = int(found.group(1)) - minor = int(found.group(2)) - supported.append( (major, minor) ) + + for line in config.python_version_classifiers: + found = re.search(pattern, line) + if found: + major = int(found.group(1)) + minor = int(found.group(2)) + supported.append( (major, minor) ) this_py = sys.version_info[:2] if this_py not in supported: - print("only these python versions are supported:", supported) + print("Unsupported python version detected. Only these python versions are supported: {}" + .format(supported)) sys.exit(1) -check_allowed_python_version() qt_src_dir = '' -# This is used automatically by distutils.command.install object, to -# specify final installation location. -OPTION_FINAL_INSTALL_PREFIX = option_value("prefix") - - if OPTION_QT_VERSION is None: OPTION_QT_VERSION = "5" if OPTION_QMAKE is None: @@ -293,8 +286,7 @@ def get_qt_version(): qt_version = qtinfo.version if not qt_version: - log.error("Failed to query the Qt version with qmake {0}".format( - self.qtinfo.qmake_command)) + log.error("Failed to query the Qt version with qmake {0}".format(qtinfo.qmake_command)) sys.exit(1) if LooseVersion(qtinfo.version) < LooseVersion("5.7"): @@ -304,12 +296,13 @@ def get_qt_version(): return qt_version + def prepare_build(): if (os.path.isdir(".git") and not OPTION_IGNOREGIT and not OPTION_ONLYPACKAGE and not OPTION_REUSE_BUILD): prepare_sub_modules() - # Clean up temp and package folders - for n in [pyside_package_dir_name, "build"]: + # Clean up temp build folder. + for n in ["build"]: d = os.path.join(setup_script_dir, n) if os.path.isdir(d): print("Removing {}".format(d)) @@ -318,13 +311,7 @@ def prepare_build(): except Exception as e: print('***** problem removing "{}"'.format(d)) print('ignored error: {}'.format(e)) - # Prepare package folders - ppdn = pyside_package_dir_name - absolute_paths = [os.path.join(ppdn, "PySide2"), - os.path.join(ppdn, "pyside2uic")] - for pkg in absolute_paths: - pkg_dir = os.path.join(setup_script_dir, pkg) - os.makedirs(pkg_dir) + # locate Qt sources for the documentation if OPTION_QT_SRC is None: install_prefix = qtinfo.prefix_dir @@ -389,26 +376,15 @@ class PysideBuildExt(_build_ext): pass - -# pyside_build_py and pyside_install_lib are reimplemented to preserve -# symlinks when distutils / setuptools copy files to various -# directories through the different build stages. class PysideBuildPy(_build_py): def __init__(self, *args, **kwargs): _build_py.__init__(self, *args, **kwargs) - def build_package_data(self): - """Copies files from pyside_package into build/xxx directory""" - - for package, src_dir, build_dir, filenames in self.data_files: - for filename in filenames: - target = os.path.join(build_dir, filename) - self.mkpath(os.path.dirname(target)) - srcfile = os.path.abspath(os.path.join(src_dir, filename)) - # Using our own copyfile makes sure to preserve symlinks. - copyfile(srcfile, target) +# _install_lib is reimplemented to preserve +# symlinks when distutils / setuptools copy files to various +# directories from the setup tools build dir to the install dir. class PysideInstallLib(_install_lib): def __init__(self, *args, **kwargs): @@ -539,10 +515,12 @@ class PysideBuild(_build): py_prefix = get_config_var("prefix") if not py_prefix or not os.path.exists(py_prefix): py_prefix = sys.prefix + self.py_prefix = py_prefix if sys.platform == "win32": py_scripts_dir = os.path.join(py_prefix, "Scripts") else: py_scripts_dir = os.path.join(py_prefix, "bin") + self.py_scripts_dir = py_scripts_dir if py_libdir is None or not os.path.exists(py_libdir): if sys.platform == "win32": py_libdir = os.path.join(py_prefix, "libs") @@ -656,7 +634,7 @@ class PysideBuild(_build): qt_version = get_qt_version() # Update the PATH environment variable - additional_paths = [py_scripts_dir, qt_dir] + additional_paths = [self.py_scripts_dir, qt_dir] # Add Clang to path for Windows. # Revisit once Clang is bundled with Qt. @@ -685,19 +663,11 @@ class PysideBuild(_build): install_dir = os.path.join(script_dir, prefix() + "_install", "{}".format(build_name)) - # Try to ensure that tools built by this script (such as shiboken2) - # are found before any that may already be installed on the system. - update_env_path([os.path.join(install_dir, 'bin')]) - - # Tell cmake to look here for *.cmake files - os.environ['CMAKE_PREFIX_PATH'] = install_dir - self.make_path = make_path self.make_generator = make_generator self.debug = OPTION_DEBUG self.script_dir = script_dir - self.pyside_package_dir = os.path.join(self.script_dir, - pyside_package_dir_name) + self.st_build_dir = os.path.join(self.script_dir, self.build_lib) self.sources_dir = sources_dir self.build_dir = build_dir self.install_dir = install_dir @@ -709,14 +679,61 @@ class PysideBuild(_build): self.site_packages_dir = get_python_lib(1, 0, prefix=install_dir) self.build_tests = OPTION_BUILDTESTS - setuptools_install_prefix = get_python_lib(1) - if OPTION_FINAL_INSTALL_PREFIX: - setuptools_install_prefix = OPTION_FINAL_INSTALL_PREFIX - # Save the shiboken build dir path for clang deployment # purposes. self.shiboken_build_dir = os.path.join(self.build_dir, "shiboken2") + self.log_pre_build_info() + + # Prepare folders + if not os.path.exists(self.sources_dir): + log.info("Creating sources folder {}...".format(self.sources_dir)) + os.makedirs(self.sources_dir) + if not os.path.exists(self.build_dir): + log.info("Creating build folder {}...".format(self.build_dir)) + os.makedirs(self.build_dir) + if not os.path.exists(self.install_dir): + log.info("Creating install folder {}...".format(self.install_dir)) + os.makedirs(self.install_dir) + + if not (OPTION_ONLYPACKAGE + and not config.is_internal_shiboken_generator_build_and_part_of_top_level_all()): + # Build extensions + for ext in config.get_buildable_extensions(): + self.build_extension(ext) + + if OPTION_BUILDTESTS: + # we record the latest successful build and note the + # build directory for supporting the tests. + timestamp = time.strftime('%Y-%m-%d_%H%M%S') + build_history = os.path.join(setup_script_dir, 'build_history') + unique_dir = os.path.join(build_history, timestamp) + os.makedirs(unique_dir) + fpath = os.path.join(unique_dir, 'build_dir.txt') + with open(fpath, 'w') as f: + print(build_dir, file=f) + log.info("Created {}".format(build_history)) + + if not OPTION_SKIP_PACKAGING: + # Build patchelf if needed + self.build_patchelf() + + # Prepare packages + self.prepare_packages() + + # Build packages + _build.run(self) + else: + log.info("Skipped preparing and building packages.") + log.info('*** Build completed') + + def log_pre_build_info(self): + if config.is_internal_shiboken_generator_build_and_part_of_top_level_all(): + return + + setuptools_install_prefix = get_python_lib(1) + if OPTION_FINAL_INSTALL_PREFIX: + setuptools_install_prefix = OPTION_FINAL_INSTALL_PREFIX log.info("=" * 30) log.info("Package version: {}".format(get_package_version())) log.info("Build type: {}".format(self.build_type)) @@ -726,40 +743,34 @@ class PysideBuild(_build): log.info("Make generator: {}".format(self.make_generator)) log.info("Make jobs: {}".format(OPTION_JOBS)) log.info("-" * 3) - log.info("setup.py directory: {}".format(self.script_dir)) log.info("Build scripts directory: {}".format(build_scripts_dir)) log.info("Sources directory: {}".format(self.sources_dir)) - log.info(dedent(""" - Building PySide2 will create and touch directories + Building {st_package_name} will create and touch directories in the following order: make build directory (py*_build/*/*) -> make install directory (py*_install/*/*) -> - {} directory (pyside_package/*) -> setuptools build directory (build/*/*) -> setuptools install directory (usually path-installed-python/lib/python*/site-packages/*) - """).format(pyside_package_dir_name)) - + """).format(st_package_name=config.package_name())) log.info("make build directory: {}".format(self.build_dir)) log.info("make install directory: {}".format(self.install_dir)) - log.info("{} directory: {}".format(pyside_package_dir_name, - self.pyside_package_dir)) - log.info("setuptools build directory: {}".format( - os.path.join(self.script_dir, "build"))) - log.info("setuptools install directory: {}".format( - setuptools_install_prefix)) - log.info("make-installed site-packages directory: {} \n" - " (only relevant for copying files from " - "'make install directory' to '{} directory'".format( - self.site_packages_dir, pyside_package_dir_name)) + log.info("setuptools build directory: {}".format(self.st_build_dir)) + log.info("setuptools install directory: {}".format(setuptools_install_prefix)) + log.info(dedent(""" + make-installed site-packages directory: {} + (only relevant for copying files from 'make install directory' + to 'setuptools build directory' + """).format( + self.site_packages_dir)) log.info("-" * 3) log.info("Python executable: {}".format(self.py_executable)) log.info("Python includes: {}".format(self.py_include_dir)) log.info("Python library: {}".format(self.py_library)) - log.info("Python prefix: {}".format(py_prefix)) - log.info("Python scripts: {}".format(py_scripts_dir)) + log.info("Python prefix: {}".format(self.py_prefix)) + log.info("Python scripts: {}".format(self.py_scripts_dir)) log.info("-" * 3) log.info("Qt qmake: {}".format(self.qtinfo.qmake_command)) log.info("Qt version: {}".format(self.qtinfo.version)) @@ -772,52 +783,11 @@ class PysideBuild(_build): if sys.platform == 'darwin': pyside_macos_deployment_target = ( PysideBuild.macos_pyside_min_deployment_target() - ) + ) log.info("MACOSX_DEPLOYMENT_TARGET set to: {}".format( pyside_macos_deployment_target)) log.info("=" * 30) - # Prepare folders - if not os.path.exists(self.sources_dir): - log.info("Creating sources folder {}...".format(self.sources_dir)) - os.makedirs(self.sources_dir) - if not os.path.exists(self.build_dir): - log.info("Creating build folder {}...".format(self.build_dir)) - os.makedirs(self.build_dir) - if not os.path.exists(self.install_dir): - log.info("Creating install folder {}...".format(self.install_dir)) - os.makedirs(self.install_dir) - - if not OPTION_ONLYPACKAGE: - # Build extensions - for ext in contained_modules: - self.build_extension(ext) - - if OPTION_BUILDTESTS: - # we record the latest successful build and note the - # build directory for supporting the tests. - timestamp = time.strftime('%Y-%m-%d_%H%M%S') - build_history = os.path.join(setup_script_dir, 'build_history') - unique_dir = os.path.join(build_history, timestamp) - os.makedirs(unique_dir) - fpath = os.path.join(unique_dir, 'build_dir.txt') - with open(fpath, 'w') as f: - print(build_dir, file=f) - log.info("Created {}".format(build_history)) - - if not OPTION_SKIP_PACKAGING: - # Build patchelf if needed - self.build_patchelf() - - # Prepare packages - self.prepare_packages() - - # Build packages - _build.run(self) - else: - log.info("Skipped preparing and building packages.") - log.info('*** Build completed') - @staticmethod def macos_qt_min_deployment_target(): target = qtinfo.macos_min_deployment_target @@ -953,6 +923,17 @@ class PysideBuild(_build): cmake_cmd.append("-DPYTHON_EXECUTABLE={}".format(self.py_executable)) cmake_cmd.append("-DPYTHON_INCLUDE_DIR={}".format(self.py_include_dir)) cmake_cmd.append("-DPYTHON_LIBRARY={}".format(self.py_library)) + + # If a custom shiboken cmake config directory path was provided, pass it to CMake. + if OPTION_SHIBOKEN_CONFIG_DIR and config.is_internal_pyside_build(): + if os.path.exists(OPTION_SHIBOKEN_CONFIG_DIR): + log.info("Using custom provided shiboken2 installation: {}" + .format(OPTION_SHIBOKEN_CONFIG_DIR)) + cmake_cmd.append("-DShiboken2_DIR={}".format(OPTION_SHIBOKEN_CONFIG_DIR)) + else: + log.info("Custom provided shiboken2 installation not found. Path given: {}" + .format(OPTION_SHIBOKEN_CONFIG_DIR)) + if OPTION_MODULE_SUBSET: module_sub_set = '' for m in OPTION_MODULE_SUBSET.split(','): @@ -1014,20 +995,20 @@ class PysideBuild(_build): cmake_cmd.append("-DPYSIDE_QT_CONF_PREFIX={}".format( pyside_qt_conf_prefix)) - # Pass package version to CMake, so this string can be - # embedded into _config.py file. - package_version = get_package_version() - cmake_cmd.append("-DPYSIDE_SETUP_PY_PACKAGE_VERSION={}".format( - package_version)) - - # In case if this is a snapshot build, also pass the - # timestamp as a separate value, because it the only - # version component that is actually generated by setup.py. - timestamp = '' - if OPTION_SNAPSHOT_BUILD: - timestamp = get_package_timestamp() - cmake_cmd.append("-DPYSIDE_SETUP_PY_PACKAGE_TIMESTAMP={}".format( - timestamp)) + # Pass package version to CMake, so this string can be + # embedded into _config.py file. + package_version = get_package_version() + cmake_cmd.append("-DPACKAGE_SETUP_PY_PACKAGE_VERSION={}".format( + package_version)) + + # In case if this is a snapshot build, also pass the + # timestamp as a separate value, because it is the only + # version component that is actually generated by setup.py. + timestamp = '' + if OPTION_SNAPSHOT_BUILD: + timestamp = get_package_timestamp() + cmake_cmd.append("-DPACKAGE_SETUP_PY_PACKAGE_TIMESTAMP={}".format( + timestamp)) if extension.lower() in ["shiboken2", "pyside2-tools"]: cmake_cmd.append("-DCMAKE_INSTALL_RPATH_USE_LINK_PATH=yes") @@ -1129,15 +1110,24 @@ class PysideBuild(_build): os.chdir(self.script_dir) def prepare_packages(self): + """ + This will copy all relevant files from the various locations in the "cmake install dir", + to the setup tools build dir (which is read from self.build_lib provided by distutils). + + After that setuptools.command.build_py is smart enough to copy everything + from the build dir to the install dir (the virtualenv site-packages for example). + """ try: - log.info("Preparing packages...") + log.info("\nPreparing setup tools build directory.\n") vars = { "site_packages_dir": self.site_packages_dir, "sources_dir": self.sources_dir, "install_dir": self.install_dir, "build_dir": self.build_dir, "script_dir": self.script_dir, - "pyside_package_dir": self.pyside_package_dir, + "st_build_dir": self.st_build_dir, + "cmake_package_name": config.package_name(), + "st_package_name": config.package_name(), "ssl_libs_dir": OPTION_OPENSSL, "py_version": self.py_version, "qt_version": self.qtinfo.version, @@ -1151,6 +1141,12 @@ class PysideBuild(_build): "qt_qml_dir": self.qtinfo.qml_dir, "target_arch": self.py_arch, } + + # Needed for correct file installation in generator build + # case. + if config.is_internal_shiboken_generator_build(): + vars['cmake_package_name'] = config.shiboken_module_option_name + os.chdir(self.script_dir) if sys.platform == "win32": @@ -1170,19 +1166,21 @@ class PysideBuild(_build): def get_built_pyside_config(self, vars): # Get config that contains list of built modules, and # SOVERSIONs of the built libraries. - pyside_package_dir = vars['pyside_package_dir'] - config_path = os.path.join(pyside_package_dir, "PySide2", "_config.py") - config = get_python_dict(config_path) - return config + st_build_dir = vars['st_build_dir'] + config_path = os.path.join(st_build_dir, config.package_name(), "_config.py") + temp_config = get_python_dict(config_path) + if 'built_modules' not in temp_config: + temp_config['built_modules'] = [] + return temp_config def is_webengine_built(self, built_modules): return ('WebEngineWidgets' in built_modules or 'WebEngineCore' in built_modules or 'WebEngine' in built_modules) - def prepare_standalone_clang(self, is_win = False): + def prepare_standalone_clang(self, is_win=False): """ - Copies the libclang library to the pyside package so that - shiboken executable works. + Copies the libclang library to the shiboken2-generator + package so that the shiboken executable works. """ log.info('Finding path to the libclang shared library.') cmake_cmd = [ @@ -1205,47 +1203,54 @@ class PysideBuild(_build): if not clang_lib_path: raise RuntimeError("Could not find the location of the libclang " - "library inside the CMake cache file.") + "library inside the CMake cache file.") - target_name = None if is_win: # clang_lib_path points to the static import library # (lib/libclang.lib), whereas we want to copy the shared # library (bin/libclang.dll). - clang_lib_path = re.sub(r'lib/libclang.lib$', 'bin/libclang.dll', - clang_lib_path) + clang_lib_path = re.sub(r'lib/libclang.lib$', + 'bin/libclang.dll', + clang_lib_path) else: - if sys.platform != 'darwin' and os.path.islink(clang_lib_path): - # On Linux, we get "libclang.so" from CMake which is - # a symlink: - # libclang.so -> libclang.so.6 -> libclang.so.6.0. - # shiboken2 links against libclang.so.6. So, we - # determine the target name by resolving just - # one symlink (note: os.path.realpath() resolves all). - target_name = os.readlink(clang_lib_path) - # We want to resolve any symlink on Linux and macOS, and - # copy the actual file. - clang_lib_path = os.path.realpath(clang_lib_path) - - if not target_name: - target_name = os.path.basename(clang_lib_path) - - # Path to directory containing libclang. - clang_lib_dir = os.path.dirname(clang_lib_path) - - # The destination will be the package folder near the other - # extension modules. - destination_dir = "{}/PySide2".format(os.path.join(self.script_dir, - 'pyside_package')) + # shiboken2 links against libclang.so.6 or a similarly + # named library. + # If the linked against library is a symlink, resolve + # the symlink once (but not all the way to the real + # file) on Linux and macOS, + # so that we get the path to the "SO version" symlink + # (the one used as the install name in the shared library + # dependency section). + # E.g. On Linux libclang.so -> libclang.so.6 -> + # libclang.so.6.0. + # "libclang.so.6" is the name we want for the copied file. + if os.path.islink(clang_lib_path): + link_target = os.readlink(clang_lib_path) + if os.path.isabs(link_target): + clang_lib_path = link_target + else: + # link_target is relative, transform to absolute. + clang_lib_path = os.path.join(os.path.dirname(clang_lib_path), link_target) + clang_lib_path = os.path.abspath(clang_lib_path) + + # The destination will be the shiboken package folder. + vars = {} + vars['st_build_dir'] = self.st_build_dir + vars['st_package_name'] = config.package_name() + destination_dir = "{st_build_dir}/{st_package_name}".format(**vars) + if os.path.exists(clang_lib_path): - log.info('Copying libclang shared library {} to the package folder as {}.'.format( - clang_lib_path, target_name)) basename = os.path.basename(clang_lib_path) - destination_path = os.path.join(destination_dir, target_name) + log.info('Copying libclang shared library {} to the package folder as {}.'.format( + clang_lib_path, basename)) + destination_path = os.path.join(destination_dir, basename) # Need to modify permissions in case file is not writable # (a reinstall would cause a permission denied error). - copyfile(clang_lib_path, destination_path, make_writable_by_owner=True) + copyfile(clang_lib_path, + destination_path, + force_copy_symlink=True, + make_writable_by_owner=True) else: raise RuntimeError("Error copying libclang library " "from {} to {}. ".format( @@ -1265,18 +1270,17 @@ class PysideBuild(_build): else: # Add rpath values pointing to $ORIGIN and the # installed qt lib directory. - local_rpath = '$ORIGIN/' - qt_lib_dir = self.qtinfo.libs_dir + final_rpath = self.qtinfo.libs_dir if OPTION_STANDALONE: - qt_lib_dir = "$ORIGIN/Qt/lib" - final_rpath = local_rpath + ':' + qt_lib_dir - cmd = [self._patchelf_path, '--set-rpath', final_rpath, srcpath] - if run_process(cmd) != 0: - raise RuntimeError("Error patching rpath in " + srcpath) + final_rpath = "$ORIGIN/Qt/lib" + override = OPTION_STANDALONE + linux_fix_rpaths_for_library(self._patchelf_path, srcpath, final_rpath, + override=override) elif sys.platform == 'darwin': pyside_libs = [lib for lib in os.listdir( package_path) if filter_match(lib, ["*.so", "*.dylib"])] + def rpath_cmd(srcpath): final_rpath = '' # Command line rpath option takes precedence over @@ -1308,15 +1312,6 @@ class PysideBuild(_build): "updated rpath (OS/X) in {}.".format(srcpath)) -try: - with open(os.path.join(setup_script_dir, 'README.rst')) as f: - README = f.read() - with open(os.path.join(setup_script_dir, 'CHANGES.rst')) as f: - CHANGES = f.read() -except IOError: - README = CHANGES = '' - - cmd_class_dict = { 'build': PysideBuild, 'build_py': PysideBuildPy, diff --git a/build_scripts/options.py b/build_scripts/options.py index fd8b0718e..daf3bb00e 100644 --- a/build_scripts/options.py +++ b/build_scripts/options.py @@ -38,16 +38,88 @@ ############################################################################# from __future__ import print_function +import sys +import os + + +class Options(object): + def __init__(self): + + # Dictionary containing values of all the possible options. + self.dict = {} + + def has_option(self, name): + """ Returns True if argument '--name' was passed on the command + line. """ + try: + sys.argv.remove("--{}".format(name)) + self.dict[name] = True + return True + except ValueError: + pass + return False + + def option_value(self, name, remove=True): + """ + Returns the value of a command line option or environment + variable. + + :param name: The name of the command line option or environment + variable. + + :param remove: Whether the option and its value should be + removed from sys.argv. Useful when there's a need to query for + the value and also pass it along to setuptools for example. + + :return: Either the option value or None. + """ + for index, option in enumerate(sys.argv): + if option == '--' + name: + if index + 1 >= len(sys.argv): + raise RuntimeError("The option {} requires a value".format(option)) + value = sys.argv[index + 1] + + if remove: + sys.argv[index:index + 2] = [] + + self.dict[name] = value + return value + + if option.startswith('--' + name + '='): + value = option[len(name) + 3:] + + if remove: + sys.argv[index:index + 1] = [] + + self.dict[name] = value + return value + + env_val = os.getenv(name.upper().replace('-', '_')) + self.dict[name] = env_val + return env_val + + +options = Options() + + +def has_option(name): + return options.has_option(name) + + +def option_value(*args,**kwargs): + return options.option_value(*args,**kwargs) -from .utils import has_option, option_value # Declare options +OPTION_BUILD_TYPE = option_value("build-type") +OPTION_INTERNAL_BUILD_TYPE = option_value("internal-build-type") OPTION_DEBUG = has_option("debug") OPTION_RELWITHDEBINFO = has_option('relwithdebinfo') OPTION_QMAKE = option_value("qmake") OPTION_QT_VERSION = option_value("qt") OPTION_CMAKE = option_value("cmake") OPTION_OPENSSL = option_value("openssl") +OPTION_SHIBOKEN_CONFIG_DIR = option_value("shiboken-config-dir") OPTION_ONLYPACKAGE = has_option("only-package") OPTION_STANDALONE = has_option("standalone") OPTION_MAKESPEC = option_value("make-spec") @@ -82,3 +154,7 @@ OPTION_SANITIZE_ADDRESS = has_option("sanitize-address") OPTION_SNAPSHOT_BUILD = has_option("snapshot-build") OPTION_LIMITED_API = option_value("limited-api") OPTION_PACKAGE_TIMESTAMP = option_value("package-timestamp") + +# This is used automatically by distutils.command.install object, to +# specify the final installation location. +OPTION_FINAL_INSTALL_PREFIX = option_value("prefix", remove=False) diff --git a/build_scripts/platforms/linux.py b/build_scripts/platforms/linux.py index 4c38fef6c..067179cdc 100644 --- a/build_scripts/platforms/linux.py +++ b/build_scripts/platforms/linux.py @@ -37,76 +37,100 @@ ## ############################################################################# -from ..options import * from ..utils import copydir, copyfile, copy_icu_libs, find_files_using_glob +from ..config import config -def prepare_standalone_package_linux(self, executables, vars): + +def prepare_standalone_package_linux(self, vars): built_modules = vars['built_modules'] - # <qt>/lib/* -> <setup>/PySide2/Qt/lib - destination_lib_dir = "{pyside_package_dir}/PySide2/Qt/lib" + constrain_modules = None + copy_plugins = True + copy_qml = True + copy_translations = True + copy_qt_conf = True + should_copy_icu_libs = True + + if config.is_internal_shiboken_generator_build(): + constrain_modules = ["Core", "Network", "Xml", "XmlPatterns"] + copy_plugins = False + copy_qml = False + copy_translations = False + copy_qt_conf = False + should_copy_icu_libs = False + + # <qt>/lib/* -> <setup>/{st_package_name}/Qt/lib + destination_lib_dir = "{st_build_dir}/{st_package_name}/Qt/lib" + + accepted_modules = ['libQt5*.so.?'] + if constrain_modules: + accepted_modules = ["libQt5" + module + "*.so.?" for module in constrain_modules] + accepted_modules.append("libicu*.so.??") + copydir("{qt_lib_dir}", destination_lib_dir, - filter=[ - "libQt5*.so.?", - "libicu*.so.??", - ], - recursive=False, vars=vars, force_copy_symlinks=True) - - # Check if ICU libraries were copied over to the destination - # Qt libdir. - resolved_destination_lib_dir = destination_lib_dir.format(**vars) - maybe_icu_libs = find_files_using_glob(resolved_destination_lib_dir, - "libicu*") - - # If no ICU libraries are present in the Qt libdir (like when - # Qt is built against system ICU, or in the Coin CI where ICU - # libs are in a different directory) try to find out / resolve - # which ICU libs are used by QtCore (if used at all) using a - # custom written ldd, and copy the ICU libs to the Pyside Qt - # dir if necessary. We choose the QtCore lib to inspect, by - # checking which QtCore library the shiboken2 executable uses. - if not maybe_icu_libs: - copy_icu_libs(self._patchelf_path, resolved_destination_lib_dir) + filter=accepted_modules, + recursive=False, vars=vars, force_copy_symlinks=True) + + if should_copy_icu_libs: + # Check if ICU libraries were copied over to the destination + # Qt libdir. + resolved_destination_lib_dir = destination_lib_dir.format(**vars) + maybe_icu_libs = find_files_using_glob(resolved_destination_lib_dir, + "libicu*") + + # If no ICU libraries are present in the Qt libdir (like when + # Qt is built against system ICU, or in the Coin CI where ICU + # libs are in a different directory) try to find out / resolve + # which ICU libs are used by QtCore (if used at all) using a + # custom written ldd, and copy the ICU libs to the Pyside Qt + # dir if necessary. We choose the QtCore lib to inspect, by + # checking which QtCore library the shiboken2 executable uses. + if not maybe_icu_libs: + copy_icu_libs(self._patchelf_path, resolved_destination_lib_dir) if self.is_webengine_built(built_modules): copydir("{qt_lib_execs_dir}", - "{pyside_package_dir}/PySide2/Qt/libexec", + "{st_build_dir}/{st_package_name}/Qt/libexec", filter=None, recursive=False, vars=vars) copydir("{qt_prefix_dir}/resources", - "{pyside_package_dir}/PySide2/Qt/resources", + "{st_build_dir}/{st_package_name}/Qt/resources", filter=None, recursive=False, vars=vars) - # <qt>/plugins/* -> <setup>/PySide2/Qt/plugins - copydir("{qt_plugins_dir}", - "{pyside_package_dir}/PySide2/Qt/plugins", - filter=["*.so"], - recursive=True, - vars=vars) - - # <qt>/qml/* -> <setup>/PySide2/Qt/qml - copydir("{qt_qml_dir}", - "{pyside_package_dir}/PySide2/Qt/qml", - filter=None, - force=False, - recursive=True, - ignore=["*.so.debug"], - vars=vars) - - # <qt>/translations/* -> <setup>/PySide2/Qt/translations - - copydir("{qt_translations_dir}", - "{pyside_package_dir}/PySide2/Qt/translations", - filter=["*.qm", "*.pak"], - force=False, - vars=vars) - - # Copy the qt.conf file to libexec. - copyfile( - "{build_dir}/pyside2/PySide2/qt.conf", - "{pyside_package_dir}/PySide2/Qt/libexec", - vars=vars) + if copy_plugins: + # <qt>/plugins/* -> <setup>/{st_package_name}/Qt/plugins + copydir("{qt_plugins_dir}", + "{st_build_dir}/{st_package_name}/Qt/plugins", + filter=["*.so"], + recursive=True, + vars=vars) + + if copy_qml: + # <qt>/qml/* -> <setup>/{st_package_name}/Qt/qml + copydir("{qt_qml_dir}", + "{st_build_dir}/{st_package_name}/Qt/qml", + filter=None, + force=False, + recursive=True, + ignore=["*.so.debug"], + vars=vars) + + if copy_translations: + # <qt>/translations/* -> + # <setup>/{st_package_name}/Qt/translations + copydir("{qt_translations_dir}", + "{st_build_dir}/{st_package_name}/Qt/translations", + filter=["*.qm", "*.pak"], + force=False, + vars=vars) + + if copy_qt_conf: + # Copy the qt.conf file to libexec. + copyfile( + "{build_dir}/pyside2/{st_package_name}/qt.conf", + "{st_build_dir}/{st_package_name}/Qt/libexec", + vars=vars) diff --git a/build_scripts/platforms/macos.py b/build_scripts/platforms/macos.py index 936f4ca90..49f02754d 100644 --- a/build_scripts/platforms/macos.py +++ b/build_scripts/platforms/macos.py @@ -37,12 +37,29 @@ ## ############################################################################# -import fnmatch, os +import fnmatch +import os from ..utils import copydir, copyfile, macos_fix_rpaths_for_library +from ..config import config -def prepare_standalone_package_macos(self, executables, vars): + +def prepare_standalone_package_macos(self, vars): built_modules = vars['built_modules'] + constrain_modules = None + copy_plugins = True + copy_qml = True + copy_translations = True + copy_qt_conf = True + + if config.is_internal_shiboken_generator_build(): + constrain_modules = ["Core", "Network", "Xml", "XmlPatterns"] + constrain_frameworks = ['Qt' + name + '.framework' for name in constrain_modules] + copy_plugins = False + copy_qml = False + copy_translations = False + copy_qt_conf = False + # Directory filter for skipping unnecessary files. def general_dir_filter(dir_name, parent_full_path, dir_full_path): if fnmatch.fnmatch(dir_name, "*.dSYM"): @@ -52,6 +69,7 @@ def prepare_standalone_package_macos(self, executables, vars): # Filter out debug plugins and qml plugins in the # debug_and_release config. no_copy_debug = True + def file_variant_filter(file_name, file_full_path): if self.qtinfo.build_type != 'debug_and_release': return True @@ -59,17 +77,16 @@ def prepare_standalone_package_macos(self, executables, vars): return False return True - # <qt>/lib/* -> <setup>/PySide2/Qt/lib + # <qt>/lib/* -> <setup>/{st_package_name}/Qt/lib if self.qt_is_framework_build(): - framework_built_modules = [ - 'Qt' + name + '.framework' for name in built_modules] - - def framework_dir_filter(dir_name, parent_full_path, - dir_full_path): + def framework_dir_filter(dir_name, parent_full_path, dir_full_path): if '.framework' in dir_name: - if dir_name.startswith('QtWebEngine') and not \ - self.is_webengine_built(built_modules): + if (dir_name.startswith('QtWebEngine') and + not self.is_webengine_built(built_modules)): + return False + if constrain_modules and dir_name not in constrain_frameworks: return False + if dir_name in ['Headers', 'fonts']: return False if dir_full_path.endswith('Versions/Current'): @@ -84,6 +101,7 @@ def prepare_standalone_package_macos(self, executables, vars): # Filter out debug frameworks in the # debug_and_release config. no_copy_debug = True + def framework_variant_filter(file_name, file_full_path): if self.qtinfo.build_type != 'debug_and_release': return True @@ -93,7 +111,7 @@ def prepare_standalone_package_macos(self, executables, vars): return False return True - copydir("{qt_lib_dir}", "{pyside_package_dir}/PySide2/Qt/lib", + copydir("{qt_lib_dir}", "{st_build_dir}/{st_package_name}/Qt/lib", recursive=True, vars=vars, ignore=["*.la", "*.a", "*.cmake", "*.pc", "*.prl"], dir_filter_function=framework_dir_filter, @@ -104,7 +122,7 @@ def prepare_standalone_package_macos(self, executables, vars): # from Versions/5/Helpers, thus adding two more levels of # directory hierarchy. if self.is_webengine_built(built_modules): - qt_lib_path = "{pyside_package_dir}/PySide2/Qt/lib".format( + qt_lib_path = "{st_build_dir}/{st_package_name}/Qt/lib".format( **vars) bundle = "QtWebEngineCore.framework/Helpers/" bundle += "QtWebEngineProcess.app" @@ -120,9 +138,11 @@ def prepare_standalone_package_macos(self, executables, vars): if 'WebKit' not in built_modules: ignored_modules.extend(['libQt5WebKit*.dylib']) accepted_modules = ['libQt5*.5.dylib'] + if constrain_modules: + accepted_modules = ["libQt5" + module + "*.5.dylib" for module in constrain_modules] copydir("{qt_lib_dir}", - "{pyside_package_dir}/PySide2/Qt/lib", + "{st_build_dir}/{st_package_name}/Qt/lib", filter=accepted_modules, ignore=ignored_modules, file_filter_function=file_variant_filter, @@ -130,53 +150,58 @@ def prepare_standalone_package_macos(self, executables, vars): if self.is_webengine_built(built_modules): copydir("{qt_lib_execs_dir}", - "{pyside_package_dir}/PySide2/Qt/libexec", + "{st_build_dir}/{st_package_name}/Qt/libexec", filter=None, recursive=False, vars=vars) copydir("{qt_prefix_dir}/resources", - "{pyside_package_dir}/PySide2/Qt/resources", + "{st_build_dir}/{st_package_name}/Qt/resources", filter=None, recursive=False, vars=vars) # Fix rpath for WebEngine process executable. - pyside_package_dir = vars['pyside_package_dir'] - qt_libexec_path = "{}/PySide2/Qt/libexec".format(pyside_package_dir) + qt_libexec_path = "{st_build_dir}/{st_package_name}/Qt/libexec".format(**vars) binary = "QtWebEngineProcess" final_path = os.path.join(qt_libexec_path, binary) rpath = "@loader_path/../lib" macos_fix_rpaths_for_library(final_path, rpath) - # Copy the qt.conf file to libexec. - copyfile( - "{build_dir}/pyside2/PySide2/qt.conf", - "{pyside_package_dir}/PySide2/Qt/libexec", - vars=vars) + if copy_qt_conf: + # Copy the qt.conf file to libexec. + copyfile( + "{build_dir}/pyside2/{st_package_name}/qt.conf", + "{st_build_dir}/{st_package_name}/Qt/libexec", + vars=vars) + + if copy_plugins: + # <qt>/plugins/* -> <setup>/{st_package_name}/Qt/plugins + copydir("{qt_plugins_dir}", + "{st_build_dir}/{st_package_name}/Qt/plugins", + filter=["*.dylib"], + recursive=True, + dir_filter_function=general_dir_filter, + file_filter_function=file_variant_filter, + vars=vars) + - # <qt>/plugins/* -> <setup>/PySide2/Qt/plugins - copydir("{qt_plugins_dir}", - "{pyside_package_dir}/PySide2/Qt/plugins", - filter=["*.dylib"], - recursive=True, - dir_filter_function=general_dir_filter, - file_filter_function=file_variant_filter, - vars=vars) - - # <qt>/qml/* -> <setup>/PySide2/Qt/qml - copydir("{qt_qml_dir}", - "{pyside_package_dir}/PySide2/Qt/qml", - filter=None, - recursive=True, - force=False, - dir_filter_function=general_dir_filter, - file_filter_function=file_variant_filter, - vars=vars) - - # <qt>/translations/* -> <setup>/PySide2/Qt/translations - copydir("{qt_translations_dir}", - "{pyside_package_dir}/PySide2/Qt/translations", - filter=["*.qm", "*.pak"], - force=False, - vars=vars) + if copy_qml: + # <qt>/qml/* -> <setup>/{st_package_name}/Qt/qml + copydir("{qt_qml_dir}", + "{st_build_dir}/{st_package_name}/Qt/qml", + filter=None, + recursive=True, + force=False, + dir_filter_function=general_dir_filter, + file_filter_function=file_variant_filter, + vars=vars) + + if copy_translations: + # <qt>/translations/* -> + # <setup>/{st_package_name}/Qt/translations + copydir("{qt_translations_dir}", + "{st_build_dir}/{st_package_name}/Qt/translations", + filter=["*.qm", "*.pak"], + force=False, + vars=vars) diff --git a/build_scripts/platforms/unix.py b/build_scripts/platforms/unix.py index c93bbbbbe..7dce11612 100644 --- a/build_scripts/platforms/unix.py +++ b/build_scripts/platforms/unix.py @@ -37,69 +37,31 @@ ## ############################################################################# -import os, re, sys +import os +import sys from .linux import prepare_standalone_package_linux from .macos import prepare_standalone_package_macos + +from ..config import config from ..options import * from ..utils import copydir, copyfile, rmtree, makefile from ..utils import regenerate_qt_resources + def prepare_packages_posix(self, vars): executables = [] - # <build>/shiboken2/doc/html/* -> - # <setup>/PySide2/docs/shiboken2 - copydir( - "{build_dir}/shiboken2/doc/html", - "{pyside_package_dir}/PySide2/docs/shiboken2", - force=False, vars=vars) - # <install>/lib/site-packages/PySide2/* -> <setup>/PySide2 - copydir( - "{site_packages_dir}/PySide2", - "{pyside_package_dir}/PySide2", - vars=vars) - # <install>/lib/site-packages/shiboken2.so -> - # <setup>/PySide2/shiboken2.so - shiboken_module_name = 'shiboken2.so' - shiboken_src_path = "{site_packages_dir}".format(**vars) - maybe_shiboken_names = [f for f in os.listdir(shiboken_src_path) - if re.match(r'shiboken.*\.so', f)] - if maybe_shiboken_names: - shiboken_module_name = maybe_shiboken_names[0] - vars.update({'shiboken_module_name': shiboken_module_name}) - copyfile( - "{site_packages_dir}/{shiboken_module_name}", - "{pyside_package_dir}/PySide2/{shiboken_module_name}", - vars=vars) - # <install>/lib/site-packages/pyside2uic/* -> - # <setup>/pyside2uic + + # <install>/lib/site-packages/{st_package_name}/* -> + # <setup>/{st_package_name} + # This copies the module .so/.dylib files and various .py files + # (__init__, config, git version, etc.) copydir( - "{site_packages_dir}/pyside2uic", - "{pyside_package_dir}/pyside2uic", - force=False, vars=vars) - if sys.version_info[0] > 2: - rmtree("{pyside_package_dir}/pyside2uic/port_v2".format(**vars)) - else: - rmtree("{pyside_package_dir}/pyside2uic/port_v3".format(**vars)) - # <install>/bin/pyside2-uic -> PySide2/scripts/uic.py - makefile( - "{pyside_package_dir}/PySide2/scripts/__init__.py", + "{site_packages_dir}/{st_package_name}", + "{st_build_dir}/{st_package_name}", vars=vars) - copyfile( - "{install_dir}/bin/pyside2-uic", - "{pyside_package_dir}/PySide2/scripts/uic.py", - force=False, vars=vars) - # <install>/bin/* -> PySide2/ - executables.extend(copydir( - "{install_dir}/bin/", - "{pyside_package_dir}/PySide2", - filter=[ - "pyside2-lupdate", - "pyside2-rcc", - "shiboken2", - ], - recursive=False, vars=vars)) - # <install>/lib/lib* -> PySide2/ - config = self.get_built_pyside_config(vars) + + generated_config = self.get_built_pyside_config(vars) + def adjusted_lib_name(name, version): postfix = '' if sys.platform.startswith('linux'): @@ -107,61 +69,144 @@ def prepare_packages_posix(self, vars): elif sys.platform == 'darwin': postfix = '.' + version + '.dylib' return name + postfix - copydir( - "{install_dir}/lib/", - "{pyside_package_dir}/PySide2", - filter=[ - adjusted_lib_name("libpyside*", - config['pyside_library_soversion']), - adjusted_lib_name("libshiboken*", - config['shiboken_library_soversion']), - ], - recursive=False, vars=vars, force_copy_symlinks=True) - # <install>/share/PySide2/typesystems/* -> - # <setup>/PySide2/typesystems - copydir( - "{install_dir}/share/PySide2/typesystems", - "{pyside_package_dir}/PySide2/typesystems", - vars=vars) - # <install>/include/* -> <setup>/PySide2/include - copydir( - "{install_dir}/include", - "{pyside_package_dir}/PySide2/include", - vars=vars) - # <source>/pyside2/PySide2/support/* -> - # <setup>/PySide2/support/* - copydir( - "{build_dir}/pyside2/PySide2/support", - "{pyside_package_dir}/PySide2/support", - vars=vars) - if not OPTION_NOEXAMPLES: - # examples/* -> <setup>/PySide2/examples - copydir(os.path.join(self.script_dir, "examples"), - "{pyside_package_dir}/PySide2/examples", - force=False, vars=vars) - # Re-generate examples Qt resource files for Python 3 - # compatibility - if sys.version_info[0] == 3: - examples_path = "{pyside_package_dir}/PySide2/examples".format( - **vars) - pyside_rcc_path = "{install_dir}/bin/pyside2-rcc".format( - **vars) - pyside_rcc_options = '-py3' - regenerate_qt_resources(examples_path, pyside_rcc_path, - pyside_rcc_options) + + if config.is_internal_shiboken_module_build(): + # <build>/shiboken2/doc/html/* -> + # <setup>/{st_package_name}/docs/shiboken2 + copydir( + "{build_dir}/shiboken2/doc/html", + "{st_build_dir}/{st_package_name}/docs/shiboken2", + force=False, vars=vars) + + # <install>/lib/lib* -> {st_package_name}/ + copydir( + "{install_dir}/lib/", + "{st_build_dir}/{st_package_name}", + filter=[ + adjusted_lib_name("libshiboken*", + generated_config['shiboken_library_soversion']), + ], + recursive=False, vars=vars, force_copy_symlinks=True) + + if config.is_internal_shiboken_generator_build(): + # <install>/bin/* -> {st_package_name}/ + executables.extend(copydir( + "{install_dir}/bin/", + "{st_build_dir}/{st_package_name}", + filter=[ + "shiboken2", + ], + recursive=False, vars=vars)) + + # Used to create scripts directory. + makefile( + "{st_build_dir}/{st_package_name}/scripts/shiboken_tool.py", + vars=vars) + + # For setting up setuptools entry points. + copyfile( + "{install_dir}/bin/shiboken_tool.py", + "{st_build_dir}/{st_package_name}/scripts/shiboken_tool.py", + force=False, vars=vars) + + if config.is_internal_shiboken_generator_build() or config.is_internal_pyside_build(): + # <install>/include/* -> <setup>/{st_package_name}/include + copydir( + "{install_dir}/include/{cmake_package_name}", + "{st_build_dir}/{st_package_name}/include", + vars=vars) + + if config.is_internal_pyside_build(): + # <install>/lib/site-packages/pyside2uic/* -> + # <setup>/pyside2uic + copydir( + "{site_packages_dir}/pyside2uic", + "{st_build_dir}/pyside2uic", + force=False, vars=vars) + if sys.version_info[0] > 2: + rmtree("{st_build_dir}/pyside2uic/port_v2".format(**vars)) + else: + rmtree("{st_build_dir}/pyside2uic/port_v3".format(**vars)) + + # <install>/bin/pyside2-uic -> {st_package_name}/scripts/uic.py + makefile( + "{st_build_dir}/{st_package_name}/scripts/__init__.py", + vars=vars) + copyfile( + "{install_dir}/bin/pyside2-uic", + "{st_build_dir}/{st_package_name}/scripts/uic.py", + force=False, vars=vars) + + # For setting up setuptools entry points + copyfile( + "{install_dir}/bin/pyside_tool.py", + "{st_build_dir}/{st_package_name}/scripts/pyside_tool.py", + force=False, vars=vars) + + # <install>/bin/* -> {st_package_name}/ + executables.extend(copydir( + "{install_dir}/bin/", + "{st_build_dir}/{st_package_name}", + filter=[ + "pyside2-lupdate", + "pyside2-rcc", + ], + recursive=False, vars=vars)) + + # <install>/lib/lib* -> {st_package_name}/ + copydir( + "{install_dir}/lib/", + "{st_build_dir}/{st_package_name}", + filter=[ + adjusted_lib_name("libpyside*", + generated_config['pyside_library_soversion']), + ], + recursive=False, vars=vars, force_copy_symlinks=True) + + # <install>/share/{st_package_name}/typesystems/* -> + # <setup>/{st_package_name}/typesystems + copydir( + "{install_dir}/share/{st_package_name}/typesystems", + "{st_build_dir}/{st_package_name}/typesystems", + vars=vars) + + # <source>/pyside2/{st_package_name}/support/* -> + # <setup>/{st_package_name}/support/* + copydir( + "{build_dir}/pyside2/{st_package_name}/support", + "{st_build_dir}/{st_package_name}/support", + vars=vars) + + if not OPTION_NOEXAMPLES: + # examples/* -> <setup>/{st_package_name}/examples + copydir(os.path.join(self.script_dir, "examples"), + "{st_build_dir}/{st_package_name}/examples", + force=False, vars=vars) + # Re-generate examples Qt resource files for Python 3 + # compatibility + if sys.version_info[0] == 3: + examples_path = "{st_build_dir}/{st_package_name}/examples".format( + **vars) + pyside_rcc_path = "{install_dir}/bin/pyside2-rcc".format( + **vars) + pyside_rcc_options = '-py3' + regenerate_qt_resources(examples_path, pyside_rcc_path, + pyside_rcc_options) + # Copy Qt libs to package if OPTION_STANDALONE: - vars['built_modules'] = config['built_modules'] - if sys.platform == 'darwin': - prepare_standalone_package_macos(self, executables, vars) - else: - prepare_standalone_package_linux(self, executables, vars) + if config.is_internal_pyside_build() or config.is_internal_shiboken_generator_build(): + vars['built_modules'] = generated_config['built_modules'] + if sys.platform == 'darwin': + prepare_standalone_package_macos(self, vars) + else: + prepare_standalone_package_linux(self, vars) - # Copy over clang before rpath patching. - self.prepare_standalone_clang(is_win=False) + if config.is_internal_shiboken_generator_build(): + # Copy over clang before rpath patching. + self.prepare_standalone_clang(is_win=False) # Update rpath to $ORIGIN - if (sys.platform.startswith('linux') or - sys.platform.startswith('darwin')): - self.update_rpath("{pyside_package_dir}/PySide2".format(**vars), - executables) + if sys.platform.startswith('linux') or sys.platform.startswith('darwin'): + rpath_path = "{st_build_dir}/{st_package_name}".format(**vars) + self.update_rpath(rpath_path, executables) diff --git a/build_scripts/platforms/windows_desktop.py b/build_scripts/platforms/windows_desktop.py index 3bf386a17..6307238a1 100644 --- a/build_scripts/platforms/windows_desktop.py +++ b/build_scripts/platforms/windows_desktop.py @@ -38,147 +38,225 @@ ############################################################################# import functools -import os, re, sys +import os +import sys + +from ..config import config from ..options import * from ..utils import copydir, copyfile, rmtree, makefile from ..utils import regenerate_qt_resources, filter_match from ..utils import download_and_extract_7z + def prepare_packages_win32(self, vars): # For now, debug symbols will not be shipped into the package. copy_pdbs = False pdbs = [] if (self.debug or self.build_type == 'RelWithDebInfo') and copy_pdbs: pdbs = ['*.pdb'] - # <install>/lib/site-packages/PySide2/* -> <setup>/PySide2 + + # <install>/lib/site-packages/{st_package_name}/* -> + # <setup>/{st_package_name} + # This copies the module .pyd files and various .py files + # (__init__, config, git version, etc.) copydir( - "{site_packages_dir}/PySide2", - "{pyside_package_dir}/PySide2", + "{site_packages_dir}/{st_package_name}", + "{st_build_dir}/{st_package_name}", vars=vars) - built_modules = self.get_built_pyside_config(vars)['built_modules'] - # <build>/pyside2/PySide2/*.pdb -> <setup>/PySide2 - copydir( - "{build_dir}/pyside2/PySide2", - "{pyside_package_dir}/PySide2", - filter=pdbs, - recursive=False, vars=vars) + if config.is_internal_shiboken_module_build(): + # <build>/shiboken2/doc/html/* -> + # <setup>/{st_package_name}/docs/shiboken2 + copydir( + "{build_dir}/shiboken2/doc/html", + "{st_build_dir}/{st_package_name}/docs/shiboken2", + force=False, vars=vars) + + # <install>/bin/*.dll -> {st_package_name}/ + copydir( + "{install_dir}/bin/", + "{st_build_dir}/{st_package_name}", + filter=["shiboken*.dll"], + recursive=False, vars=vars) - # <build>/shiboken2/doc/html/* -> - # <setup>/PySide2/docs/shiboken2 - copydir( - "{build_dir}/shiboken2/doc/html", - "{pyside_package_dir}/PySide2/docs/shiboken2", - force=False, vars=vars) - - # <install>/lib/site-packages/shiboken2.pyd -> - # <setup>/PySide2/shiboken2.pyd - shiboken_module_name = 'shiboken2.pyd' - shiboken_src_path = "{site_packages_dir}".format(**vars) - maybe_shiboken_names = [f for f in os.listdir(shiboken_src_path) - if re.match(r'shiboken.*\.pyd', f)] - if maybe_shiboken_names: - shiboken_module_name = maybe_shiboken_names[0] - vars.update({'shiboken_module_name': shiboken_module_name}) - copyfile( - "{site_packages_dir}/{shiboken_module_name}", - "{pyside_package_dir}/PySide2/{shiboken_module_name}", - vars=vars) - # @TODO: Fix this .pdb file not to overwrite release - # {shibokengenerator}.pdb file. - # Task-number: PYSIDE-615 - copydir( - "{build_dir}/shiboken2/shibokenmodule", - "{pyside_package_dir}/PySide2", - filter=pdbs, - recursive=False, vars=vars) + # <install>/lib/*.lib -> {st_package_name}/ + copydir( + "{install_dir}/lib/", + "{st_build_dir}/{st_package_name}", + filter=["shiboken*.lib"], + recursive=False, vars=vars) - # <install>/lib/site-packages/pyside2uic/* -> - # <setup>/pyside2uic - copydir( - "{site_packages_dir}/pyside2uic", - "{pyside_package_dir}/pyside2uic", - force=False, vars=vars) - if sys.version_info[0] > 2: - rmtree("{pyside_package_dir}/pyside2uic/port_v2".format(**vars)) - else: - rmtree("{pyside_package_dir}/pyside2uic/port_v3".format(**vars)) + # @TODO: Fix this .pdb file not to overwrite release + # {shibokengenerator}.pdb file. + # Task-number: PYSIDE-615 + copydir( + "{build_dir}/shiboken2/shibokenmodule", + "{st_build_dir}/{st_package_name}", + filter=pdbs, + recursive=False, vars=vars) - # <install>/bin/pyside2-uic -> PySide2/scripts/uic.py - makefile( - "{pyside_package_dir}/PySide2/scripts/__init__.py", - vars=vars) - copyfile( - "{install_dir}/bin/pyside2-uic", - "{pyside_package_dir}/PySide2/scripts/uic.py", - force=False, vars=vars) + # pdb files for libshiboken and libpyside + copydir( + "{build_dir}/shiboken2/libshiboken", + "{st_build_dir}/{st_package_name}", + filter=pdbs, + recursive=False, vars=vars) - # <install>/bin/*.exe,*.dll,*.pdb -> PySide2/ - copydir( - "{install_dir}/bin/", - "{pyside_package_dir}/PySide2", - filter=["*.exe", "*.dll"], - recursive=False, vars=vars) - # @TODO: Fix this .pdb file not to overwrite release - # {shibokenmodule}.pdb file. - # Task-number: PYSIDE-615 - copydir( - "{build_dir}/shiboken2/generator", - "{pyside_package_dir}/PySide2", - filter=pdbs, - recursive=False, vars=vars) + if config.is_internal_shiboken_generator_build(): + # <install>/bin/*.dll -> {st_package_name}/ + copydir( + "{install_dir}/bin/", + "{st_build_dir}/{st_package_name}", + filter=["shiboken*.exe"], + recursive=False, vars=vars) - # <install>/lib/*.lib -> PySide2/ - copydir( - "{install_dir}/lib/", - "{pyside_package_dir}/PySide2", - filter=["*.lib"], - recursive=False, vars=vars) + # Used to create scripts directory. + makefile( + "{st_build_dir}/{st_package_name}/scripts/shiboken_tool.py", + vars=vars) - # <install>/share/PySide2/typesystems/* -> - # <setup>/PySide2/typesystems - copydir( - "{install_dir}/share/PySide2/typesystems", - "{pyside_package_dir}/PySide2/typesystems", - vars=vars) + # For setting up setuptools entry points. + copyfile( + "{install_dir}/bin/shiboken_tool.py", + "{st_build_dir}/{st_package_name}/scripts/shiboken_tool.py", + force=False, vars=vars) + + # @TODO: Fix this .pdb file not to overwrite release + # {shibokenmodule}.pdb file. + # Task-number: PYSIDE-615 + copydir( + "{build_dir}/shiboken2/generator", + "{st_build_dir}/{st_package_name}", + filter=pdbs, + recursive=False, vars=vars) - # <install>/include/* -> <setup>/PySide2/include - copydir( - "{install_dir}/include", - "{pyside_package_dir}/PySide2/include", - vars=vars) + if config.is_internal_shiboken_generator_build() or config.is_internal_pyside_build(): + # <install>/include/* -> <setup>/{st_package_name}/include + copydir( + "{install_dir}/include/{cmake_package_name}", + "{st_build_dir}/{st_package_name}/include", + vars=vars) - # <source>/pyside2/PySide2/support/* -> - # <setup>/PySide2/support/* - copydir( - "{build_dir}/pyside2/PySide2/support", - "{pyside_package_dir}/PySide2/support", - vars=vars) + if config.is_internal_pyside_build(): + # <build>/pyside2/{st_package_name}/*.pdb -> + # <setup>/{st_package_name} + copydir( + "{build_dir}/pyside2/{st_package_name}", + "{st_build_dir}/{st_package_name}", + filter=pdbs, + recursive=False, vars=vars) + + # <install>/lib/site-packages/pyside2uic/* -> + # <setup>/pyside2uic + copydir( + "{site_packages_dir}/pyside2uic", + "{st_build_dir}/pyside2uic", + force=False, vars=vars) + if sys.version_info[0] > 2: + rmtree("{st_build_dir}/pyside2uic/port_v2".format(**vars)) + else: + rmtree("{st_build_dir}/pyside2uic/port_v3".format(**vars)) + + # <install>/bin/pyside2-uic -> {st_package_name}/scripts/uic.py + makefile( + "{st_build_dir}/{st_package_name}/scripts/__init__.py", + vars=vars) + copyfile( + "{install_dir}/bin/pyside2-uic", + "{st_build_dir}/{st_package_name}/scripts/uic.py", + force=False, vars=vars) + + # For setting up setuptools entry points + copyfile( + "{install_dir}/bin/pyside_tool.py", + "{st_build_dir}/{st_package_name}/scripts/pyside_tool.py", + force=False, vars=vars) + + # <install>/bin/*.exe,*.dll -> {st_package_name}/ + copydir( + "{install_dir}/bin/", + "{st_build_dir}/{st_package_name}", + filter=["pyside*.exe", "pyside*.dll"], + recursive=False, vars=vars) - if not OPTION_NOEXAMPLES: - # examples/* -> <setup>/PySide2/examples - copydir(os.path.join(self.script_dir, "examples"), - "{pyside_package_dir}/PySide2/examples", + # <install>/lib/*.lib -> {st_package_name}/ + copydir( + "{install_dir}/lib/", + "{st_build_dir}/{st_package_name}", + filter=["pyside*.lib"], + recursive=False, vars=vars) + + # <install>/share/{st_package_name}/typesystems/* -> + # <setup>/{st_package_name}/typesystems + copydir( + "{install_dir}/share/{st_package_name}/typesystems", + "{st_build_dir}/{st_package_name}/typesystems", + vars=vars) + + # <source>/pyside2/{st_package_name}/support/* -> + # <setup>/{st_package_name}/support/* + copydir( + "{build_dir}/pyside2/{st_package_name}/support", + "{st_build_dir}/{st_package_name}/support", + vars=vars) + + copydir( + "{build_dir}/pyside2/libpyside", + "{st_build_dir}/{st_package_name}", + filter=pdbs, + recursive=False, vars=vars) + + if not OPTION_NOEXAMPLES: + # examples/* -> <setup>/{st_package_name}/examples + copydir(os.path.join(self.script_dir, "examples"), + "{st_build_dir}/{st_package_name}/examples", + force=False, vars=vars) + # Re-generate examples Qt resource files for Python 3 + # compatibility + if sys.version_info[0] == 3: + examples_path = "{st_build_dir}/{st_package_name}/examples".format( + **vars) + pyside_rcc_path = "{install_dir}/bin/pyside2-rcc".format( + **vars) + pyside_rcc_options = '-py3' + regenerate_qt_resources(examples_path, pyside_rcc_path, + pyside_rcc_options) + + if vars['ssl_libs_dir']: + # <ssl_libs>/* -> <setup>/{st_package_name}/openssl + copydir("{ssl_libs_dir}", "{st_build_dir}/{st_package_name}/openssl", + filter=[ + "libeay32.dll", + "ssleay32.dll"], force=False, vars=vars) - # Re-generate examples Qt resource files for Python 3 - # compatibility - if sys.version_info[0] == 3: - examples_path = "{pyside_package_dir}/PySide2/examples".format( - **vars) - pyside_rcc_path = "{install_dir}/bin/pyside2-rcc".format( - **vars) - pyside_rcc_options = '-py3' - regenerate_qt_resources(examples_path, pyside_rcc_path, - pyside_rcc_options) - - # <ssl_libs>/* -> <setup>/PySide2/openssl - copydir("{ssl_libs_dir}", "{pyside_package_dir}/PySide2/openssl", - filter=[ - "libeay32.dll", - "ssleay32.dll"], - force=False, vars=vars) - - # <qt>/bin/*.dll and Qt *.exe -> <setup>/PySide2 + + if config.is_internal_pyside_build() or config.is_internal_shiboken_generator_build(): + copy_qt_artifacts(self, copy_pdbs, vars) + + +def copy_qt_artifacts(self, copy_pdbs, vars): + built_modules = self.get_built_pyside_config(vars)['built_modules'] + + constrain_modules = None + copy_plugins = True + copy_qml = True + copy_translations = True + copy_qt_conf = True + copy_qt_permanent_artifacts = True + copy_msvc_redist = False + copy_clang = False + + if config.is_internal_shiboken_generator_build(): + constrain_modules = ["Core", "Network", "Xml", "XmlPatterns"] + copy_plugins = False + copy_qml = False + copy_translations = False + copy_qt_conf = False + copy_qt_permanent_artifacts = False + copy_msvc_redist = True + copy_clang = True + + # <qt>/bin/*.dll and Qt *.exe -> <setup>/{st_package_name} qt_artifacts_permanent = [ "opengl*.dll", "d3d*.dll", @@ -189,6 +267,7 @@ def prepare_packages_win32(self, vars): "lconvert.exe", "qtdiag.exe" ] + # MSVC redistributable msvc_redist = [ "concrt140.dll", @@ -212,8 +291,14 @@ def prepare_packages_win32(self, vars): else: egl_suffix = '' qt_artifacts_egl = [a.format(egl_suffix) for a in qt_artifacts_egl] - qt_artifacts_permanent += qt_artifacts_egl - qt_artifacts_permanent += msvc_redist + + artifacts = [] + if copy_qt_permanent_artifacts: + artifacts += qt_artifacts_permanent + artifacts += qt_artifacts_egl + + if copy_msvc_redist: + artifacts += msvc_redist # Extract Qt dependency dll's when building on Qt CI # There is no proper CI env variable, so using agent launch params @@ -225,15 +310,22 @@ def prepare_packages_win32(self, vars): zip_file = "pyside_qt_deps_32.7z" download_and_extract_7z(redist_url + zip_file, "{qt_bin_dir}".format(**vars)) - copydir("{qt_bin_dir}", "{pyside_package_dir}/PySide2", - filter=qt_artifacts_permanent, - recursive=False, vars=vars) + if artifacts: + copydir("{qt_bin_dir}", + "{st_build_dir}/{st_package_name}", + filter=artifacts, recursive=False, vars=vars) - # <qt>/bin/*.dll and Qt *.pdbs -> <setup>/PySide2 part two + # <qt>/bin/*.dll and Qt *.pdbs -> <setup>/{st_package_name} part two # File filter to copy only debug or only release files. - qt_dll_patterns = ["Qt5*{}.dll", "lib*{}.dll"] - if copy_pdbs: - qt_dll_patterns += ["Qt5*{}.pdb", "lib*{}.pdb"] + if constrain_modules: + qt_dll_patterns = ["Qt5" + x + "{}.dll" for x in constrain_modules] + if copy_pdbs: + qt_dll_patterns += ["Qt5" + x + "{}.pdb" for x in constrain_modules] + else: + qt_dll_patterns = ["Qt5*{}.dll", "lib*{}.dll"] + if copy_pdbs: + qt_dll_patterns += ["Qt5*{}.pdb", "lib*{}.pdb"] + def qt_build_config_filter(patterns, file_name, file_full_path): release = [a.format('') for a in patterns] debug = [a.format('d') for a in patterns] @@ -283,56 +375,60 @@ def prepare_packages_win32(self, vars): return False qt_dll_filter = functools.partial(qt_build_config_filter, - qt_dll_patterns) - copydir("{qt_bin_dir}", "{pyside_package_dir}/PySide2", - file_filter_function=qt_dll_filter, - recursive=False, vars=vars) - - # <qt>/plugins/* -> <setup>/PySide2/plugins - plugin_dll_patterns = ["*{}.dll"] - pdb_pattern = "*{}.pdb" - if copy_pdbs: - plugin_dll_patterns += [pdb_pattern] - plugin_dll_filter = functools.partial(qt_build_config_filter, - plugin_dll_patterns) - copydir("{qt_plugins_dir}", "{pyside_package_dir}/PySide2/plugins", - file_filter_function=plugin_dll_filter, - vars=vars) + qt_dll_patterns) + copydir("{qt_bin_dir}", + "{st_build_dir}/{st_package_name}", + file_filter_function=qt_dll_filter, + recursive=False, vars=vars) - # <qt>/translations/* -> <setup>/PySide2/translations - copydir("{qt_translations_dir}", - "{pyside_package_dir}/PySide2/translations", - filter=["*.qm", "*.pak"], - force=False, - vars=vars) + if copy_plugins: + # <qt>/plugins/* -> <setup>/{st_package_name}/plugins + plugin_dll_patterns = ["*{}.dll"] + pdb_pattern = "*{}.pdb" + if copy_pdbs: + plugin_dll_patterns += [pdb_pattern] + plugin_dll_filter = functools.partial(qt_build_config_filter, + plugin_dll_patterns) + copydir("{qt_plugins_dir}", "{st_build_dir}/{st_package_name}/plugins", + file_filter_function=plugin_dll_filter, + vars=vars) - # <qt>/qml/* -> <setup>/PySide2/qml - qml_dll_patterns = ["*{}.dll"] - qml_ignore_patterns = qml_dll_patterns + [pdb_pattern] - qml_ignore = [a.format('') for a in qml_ignore_patterns] + if copy_translations: + # <qt>/translations/* -> <setup>/{st_package_name}/translations + copydir("{qt_translations_dir}", + "{st_build_dir}/{st_package_name}/translations", + filter=["*.qm", "*.pak"], + force=False, + vars=vars) - # Copy all files that are not dlls and pdbs (.qml, qmldir). - copydir("{qt_qml_dir}", "{pyside_package_dir}/PySide2/qml", - ignore=qml_ignore, - force=False, - recursive=True, - vars=vars) + if copy_qml: + # <qt>/qml/* -> <setup>/{st_package_name}/qml + qml_dll_patterns = ["*{}.dll"] + qml_ignore_patterns = qml_dll_patterns + [pdb_pattern] + qml_ignore = [a.format('') for a in qml_ignore_patterns] + + # Copy all files that are not dlls and pdbs (.qml, qmldir). + copydir("{qt_qml_dir}", "{st_build_dir}/{st_package_name}/qml", + ignore=qml_ignore, + force=False, + recursive=True, + vars=vars) - if copy_pdbs: - qml_dll_patterns += [pdb_pattern] - qml_dll_filter = functools.partial(qt_build_config_filter, - qml_dll_patterns) + if copy_pdbs: + qml_dll_patterns += [pdb_pattern] + qml_dll_filter = functools.partial(qt_build_config_filter, + qml_dll_patterns) - # Copy all dlls (and possibly pdbs). - copydir("{qt_qml_dir}", "{pyside_package_dir}/PySide2/qml", - file_filter_function=qml_dll_filter, - force=False, - recursive=True, - vars=vars) + # Copy all dlls (and possibly pdbs). + copydir("{qt_qml_dir}", "{st_build_dir}/{st_package_name}/qml", + file_filter_function=qml_dll_filter, + force=False, + recursive=True, + vars=vars) if self.is_webengine_built(built_modules): copydir("{qt_prefix_dir}/resources", - "{pyside_package_dir}/PySide2/resources", + "{st_build_dir}/{st_package_name}/resources", filter=None, recursive=False, vars=vars) @@ -340,26 +436,16 @@ def prepare_packages_win32(self, vars): filter = 'QtWebEngineProcess{}.exe'.format( 'd' if self.debug else '') copydir("{qt_bin_dir}", - "{pyside_package_dir}/PySide2", + "{st_build_dir}/{st_package_name}", filter=[filter], recursive=False, vars=vars) - # Copy the qt.conf file to prefix dir. - copyfile( - "{build_dir}/pyside2/PySide2/qt.conf", - "{pyside_package_dir}/PySide2", - vars=vars) - - self.prepare_standalone_clang(is_win=True) + if copy_qt_conf: + # Copy the qt.conf file to prefix dir. + copyfile( + "{build_dir}/pyside2/{st_package_name}/qt.conf", + "{st_build_dir}/{st_package_name}", + vars=vars) - # pdb files for libshiboken and libpyside - copydir( - "{build_dir}/shiboken2/libshiboken", - "{pyside_package_dir}/PySide2", - filter=pdbs, - recursive=False, vars=vars) - copydir( - "{build_dir}/pyside2/libpyside", - "{pyside_package_dir}/PySide2", - filter=pdbs, - recursive=False, vars=vars) + if copy_clang: + self.prepare_standalone_clang(is_win=True) diff --git a/build_scripts/setup_runner.py b/build_scripts/setup_runner.py new file mode 100644 index 000000000..709b4b25c --- /dev/null +++ b/build_scripts/setup_runner.py @@ -0,0 +1,165 @@ +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of Qt for Python. +## +## $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$ +## +############################################################################# + +import sys, os, textwrap + +from build_scripts.config import config +from build_scripts.main import get_package_version, get_setuptools_extension_modules +from build_scripts.main import cmd_class_dict +from build_scripts.options import OPTION_BUILD_TYPE, OPTION_INTERNAL_BUILD_TYPE +from build_scripts.utils import run_process + +from setuptools import setup + + +class SetupRunner(object): + def __init__(self, orig_argv): + self.invocations_list = [] + + # Keep the original args around in case we ever need to pass + # modified arguments to the sub invocations. + self.orig_argv = orig_argv + self.sub_argv = list(orig_argv) + + self.setup_script_dir = os.getcwd() + + @staticmethod + def cmd_line_argument_is_in_args(argument, args): + """ Check if command line argument was passed in args. """ + return any(arg for arg in list(args) if "--" + argument in arg) + + @staticmethod + def remove_cmd_line_argument_in_args(argument, args): + """ Remove command line argument from args. """ + return [arg for arg in list(args) if "--" + argument not in arg] + + @staticmethod + def construct_cmd_line_argument(name, value=None): + """ Constructs a command line argument given name and value. """ + if not value: + return "--{}".format(name) + return "--{}={}".format(name, value) + + @staticmethod + def construct_internal_build_type_cmd_line_argument(internal_build_type): + return SetupRunner.construct_cmd_line_argument("internal-build-type", internal_build_type) + + def add_setup_internal_invocation(self, build_type, reuse_build=False): + """ Enqueues a script sub-invocation to be executed later. """ + internal_build_type_arg = self.construct_internal_build_type_cmd_line_argument(build_type) + setup_cmd = [sys.executable] + self.sub_argv + [internal_build_type_arg] + + # Add --reuse-build option if requested and not already present. + if reuse_build and not self.cmd_line_argument_is_in_args("reuse-build", self.sub_argv): + setup_cmd.append(self.construct_cmd_line_argument("reuse-build")) + self.invocations_list.append(setup_cmd) + + def run_setup(self): + """ + Decide what kind of build is requested and then execute it. + In the top-level invocation case, the script + will spawn setup.py again (possibly multiple times). + In the internal invocation case, the script + will run setuptools.setup(). + """ + + # Prepare initial config. + config.init_config(build_type=OPTION_BUILD_TYPE, + internal_build_type=OPTION_INTERNAL_BUILD_TYPE, + cmd_class_dict=cmd_class_dict, + package_version=get_package_version(), + ext_modules=get_setuptools_extension_modules(), + setup_script_dir=self.setup_script_dir) + + # This is an internal invocation of setup.py, so start actual + # build. + if config.is_internal_invocation(): + if config.internal_build_type not in config.get_allowed_internal_build_values(): + raise RuntimeError("Invalid '{}' option given to --internal-build-type. " + .format(config.internal_build_type)) + self.run_setuptools_setup() + return + + # This is a top-level invocation of setup.py, so figure out what + # modules we will build and depending on that, call setup.py + # multiple times with different arguments. + if config.build_type not in config.get_allowed_top_level_build_values(): + raise RuntimeError("Invalid '{}' option given to --build-type. " + .format(config.build_type)) + + # Build everything: shiboken2, shiboken2-generator and PySide2. + if config.is_top_level_build_all(): + self.add_setup_internal_invocation(config.shiboken_module_option_name) + + # Reuse the shiboken build for the generator package instead + # of rebuilding it again. + self.add_setup_internal_invocation(config.shiboken_generator_option_name, + reuse_build=True) + + self.add_setup_internal_invocation(config.pyside_option_name) + + elif config.is_top_level_build_shiboken_module(): + self.add_setup_internal_invocation(config.shiboken_module_option_name) + + elif config.is_top_level_build_shiboken_generator(): + self.add_setup_internal_invocation(config.shiboken_generator_option_name) + + elif config.is_top_level_build_pyside(): + self.add_setup_internal_invocation(config.pyside_option_name) + + for cmd in self.invocations_list: + cmd_as_string = " ".join(cmd) + print("\nRunning process: {}\n".format(cmd_as_string)) + exit_code = run_process(cmd, redirect_stderr_to_stdout=False) + if exit_code != 0: + msg = textwrap.dedent(""" + setup.py invocation failed with exit code: {}.\n\n + setup.py invocation was: {} + """).format(exit_code, cmd_as_string) + raise RuntimeError(msg) + + @staticmethod + def run_setuptools_setup(): + """ + Runs setuptools.setup() once in a single setup.py + sub-invocation. + """ + + kwargs = config.setup_kwargs + setup(**kwargs) diff --git a/build_scripts/utils.py b/build_scripts/utils.py index 4c23a6279..5c5f4927d 100644 --- a/build_scripts/utils.py +++ b/build_scripts/utils.py @@ -39,17 +39,13 @@ import sys import os -import stat import re import stat import errno -import time import shutil import subprocess import fnmatch -import glob import itertools -import popenasync import glob # There is no urllib.request in Python2 @@ -58,11 +54,9 @@ try: except ImportError: import urllib -from distutils import log +import distutils.log as log from distutils.errors import DistutilsOptionError from distutils.errors import DistutilsSetupError -from distutils.spawn import spawn -from distutils.spawn import DistutilsExecError try: WindowsError @@ -70,32 +64,6 @@ except NameError: WindowsError = None -def has_option(name): - try: - sys.argv.remove("--{}".format(name)) - return True - except ValueError: - pass - return False - - -def option_value(name): - for index, option in enumerate(sys.argv): - if option == '--' + name: - if index+1 >= len(sys.argv): - raise DistutilsOptionError("The option {} requires a " - "value".format(option)) - value = sys.argv[index+1] - sys.argv[index:index+2] = [] - return value - if option.startswith('--' + name + '='): - value = option[len(name)+3:] - sys.argv[index:index+1] = [] - return value - env_val = os.getenv(name.upper().replace('-', '_')) - return env_val - - def filter_match(name, patterns): for pattern in patterns: if pattern is None: @@ -182,7 +150,6 @@ def find_vcdir(version): """ from distutils.msvc9compiler import VS_BASE from distutils.msvc9compiler import Reg - from distutils import log vsbase = VS_BASE % version try: productdir = Reg.get_value(r"{}\Setup\VC".format(vsbase), "productdir") @@ -416,13 +383,15 @@ def rmtree(dirname, ignore=False): os.chmod(path, stat.S_IRWXU| stat.S_IRWXG| stat.S_IRWXO) # 0777 func(path) else: - raise + raise IOError shutil.rmtree(dirname, ignore_errors=ignore, onerror=handle_remove_readonly) def run_process_output(args, initial_env=None): if initial_env is None: initial_env = os.environ - std_out = subprocess.Popen(args, env = initial_env, universal_newlines = 1, + std_out = subprocess.Popen(args, + env = initial_env, + universal_newlines = 1, stdout=subprocess.PIPE).stdout result = [] for raw_line in std_out.readlines(): @@ -430,54 +399,27 @@ def run_process_output(args, initial_env=None): result.append(line.rstrip()) return result -def run_process(args, initial_env=None): - def _log(buffer, check_new_line=False): - ends_with_new_line = False - if buffer.endswith('\n'): - ends_with_new_line = True - if check_new_line and buffer.find('\n') == -1: - return buffer - lines = buffer.splitlines() - buffer = '' - if check_new_line and not ends_with_new_line: - buffer = lines[-1] - lines = lines[:-1] - for line in lines: - log.info(line.rstrip('\r')) - return buffer - _log("Running process in {0}: {1}".format(os.getcwd(), - " ".join([(" " in x and '"{0}"'.format(x) or x) for x in args]))) - - if sys.platform != "win32": - try: - spawn(args) - return 0 - except DistutilsExecError: - return -1 - - shell = False - if sys.platform == "win32": - shell = True +def run_process(args, initial_env=None, redirect_stderr_to_stdout=True): + """ + Run process until completion and return the process exit code. + Prints both stdout and stderr to the console. + No output is captured. + """ + log.info("Running process in directory {0}: command {1}".format( + os.getcwd(), + " ".join([(" " in x and '"{0}"'.format(x) or x) for x in args])) + ) if initial_env is None: initial_env = os.environ - proc = popenasync.Popen(args, - stdin = subprocess.PIPE, - stdout = subprocess.PIPE, - stderr = subprocess.STDOUT, - universal_newlines = 1, - shell = shell, - env = initial_env) - - log_buffer = None; - while proc.poll() is None: - log_buffer = _log(proc.read_async(wait=0.1, e=0)) - if log_buffer: - _log(log_buffer) + kwargs = {} + kwargs['env'] = initial_env + if redirect_stderr_to_stdout: + kwargs['stderr'] = subprocess.STDOUT - proc.wait() - return proc.returncode + exit_code = subprocess.call(args, **kwargs) + return exit_code def get_environment_from_batch_command(env_cmd, initial=None): @@ -607,8 +549,7 @@ def back_tick(cmd, ret_err=False): return out, err.strip(), retcode -MACOS_OUTNAME_RE = re.compile(r'\(compatibility version [\d.]+, current version ' - '[\d.]+\)') +MACOS_OUTNAME_RE = re.compile(r'\(compatibility version [\d.]+, current version [\d.]+\)') def macos_get_install_names(libpath): """ @@ -665,6 +606,9 @@ def macos_get_rpaths(libpath): ctr += 3 return rpaths +def macos_add_rpath(rpath, library_path): + back_tick('install_name_tool -add_rpath {rpath} {library_path}'.format( + rpath=rpath, library_path=library_path)) def macos_fix_rpaths_for_library(library_path, qt_lib_dir): """ Adds required rpath load commands to given library. @@ -703,8 +647,7 @@ def macos_fix_rpaths_for_library(library_path, qt_lib_dir): break if needs_loader_path and "@loader_path" not in existing_rpath_commands: - back_tick('install_name_tool -add_rpath {rpath} {library_path}'.format( - rpath="@loader_path", library_path=library_path)) + macos_add_rpath("@loader_path", library_path) # If the library depends on a Qt library, add an rpath load comment # pointing to the Qt lib directory. @@ -738,8 +681,7 @@ def macos_add_qt_rpath(library_path, qt_lib_dir, break if needs_qt_rpath: - back_tick('install_name_tool -add_rpath {rpath} {library_path}'.format( - rpath=qt_lib_dir, library_path=library_path)) + macos_add_rpath(qt_lib_dir, library_path) # Find an executable specified by a glob pattern ('foo*') in the OS path def find_glob_in_path(pattern): @@ -754,7 +696,7 @@ def find_glob_in_path(pattern): # Locate the most recent version of llvm_config in the path. def find_llvm_config(): - version_re = re.compile('(\d+)\.(\d+)\.(\d+)') + version_re = re.compile(r'(\d+)\.(\d+)\.(\d+)') result = None last_version_string = '000000' for llvm_config in find_glob_in_path('llvm-config*'): @@ -996,6 +938,17 @@ def copy_icu_libs(patchelf, destination_lib_dir): new_rpaths_string = ":".join(rpaths) linux_set_rpaths(patchelf, qt_core_library_path, new_rpaths_string) + +def linux_run_read_elf(executable_path): + cmd = "readelf -d {}".format(executable_path) + (out, err, code) = back_tick(cmd, True) + if code != 0: + raise RuntimeError("Running `readelf -d {}` failed with error " + "output:\n {}. ".format(executable_path, err)) + lines = split_and_strip(out) + return lines + + def linux_set_rpaths(patchelf, executable_path, rpath_string): """ Patches the `executable_path` with a new rpath string. """ @@ -1005,18 +958,32 @@ def linux_set_rpaths(patchelf, executable_path, rpath_string): raise RuntimeError("Error patching rpath in {}".format( executable_path)) + +def linux_get_dependent_libraries(executable_path): + """ + Returns a list of libraries that executable_path depends on. + """ + + lines = linux_run_read_elf(executable_path) + pattern = re.compile(r"^.+?\(NEEDED\).+?\[(.+?)\]$") + + library_lines = [] + for line in lines: + match = pattern.search(line) + if match: + library_line = match.group(1) + library_lines.append(library_line) + + return library_lines + + def linux_get_rpaths(executable_path): """ Returns a list of run path values embedded in the executable or just an empty list. """ - cmd = "readelf -d {}".format(executable_path) - (out, err, code) = back_tick(cmd, True) - if code != 0: - raise RuntimeError("Running `readelf -d {}` failed with error " - "output:\n {}. ".format(executable_path, err)) - lines = split_and_strip(out) + lines = linux_run_read_elf(executable_path) pattern = re.compile(r"^.+?\(RUNPATH\).+?\[(.+?)\]$") rpath_line = None @@ -1033,6 +1000,7 @@ def linux_get_rpaths(executable_path): return rpaths + def rpaths_has_origin(rpaths): """ Return True if the specified list of rpaths has an "$ORIGIN" value @@ -1048,6 +1016,39 @@ def rpaths_has_origin(rpaths): return True return False + +def linux_needs_qt_rpath(executable_path): + """ + Returns true if library_path depends on Qt libraries. + """ + + dependencies = linux_get_dependent_libraries(executable_path) + + # Check if any library dependencies are Qt libraries (hacky). + needs_qt_rpath = False + for dep in dependencies: + if 'Qt' in dep: + needs_qt_rpath = True + break + return needs_qt_rpath + + +def linux_fix_rpaths_for_library(patchelf, executable_path, qt_rpath, override=False): + """ + Adds or overrides required rpaths in given executable / library. + """ + rpaths = ['$ORIGIN/'] + existing_rpaths = [] + if not override: + existing_rpaths = linux_get_rpaths(executable_path) + rpaths.extend(existing_rpaths) + + if linux_needs_qt_rpath(executable_path) and qt_rpath not in existing_rpaths: + rpaths.append(qt_rpath) + + rpaths_string = ':'.join(rpaths) + linux_set_rpaths(patchelf, executable_path, rpaths_string) + def memoize(function): """ Decorator to wrap a function with a memoizing callable. @@ -1076,9 +1077,27 @@ def get_python_dict(python_script_path): "file: {}.".format(python_script_path)) raise -def install_pip_dependencies(env_pip, packages): +def install_pip_wheel_package(env_pip): + # Need to install an unreleased wheel version, due to a bug that + # will generate a wheel which will not be installable. + # See https://github.com/pypa/wheel/issues/263 + wheel_url = "git+https://github.com/pypa/wheel.git@fbf3e3ada64d36ca7bb9c1422f5a1ccdba7e4dcf" + install_pip_package_from_url_specifier(env_pip, wheel_url) + +def install_pip_package_from_url_specifier(env_pip, url, upgrade=True): + args = [env_pip, "install", url] + if upgrade: + args.append("--upgrade") + args.append(url) + run_instruction(args, "Failed to install {}".format(url)) + +def install_pip_dependencies(env_pip, packages, upgrade=True): for p in packages: - run_instruction([env_pip, "install", p], "Failed to install " + p) + args = [env_pip, "install"] + if upgrade: + args.append("--upgrade") + args.append(p) + run_instruction(args, "Failed to install " + p) def get_qtci_virtualEnv(python_ver, host, hostArch, targetArch): _pExe = "python" @@ -1105,16 +1124,18 @@ def get_qtci_virtualEnv(python_ver, host, hostArch, targetArch): _pExe = "python3" return(_pExe, _env, env_pip, env_python) -def run_instruction(instruction, error): +def run_instruction(instruction, error, initial_env=None): + if initial_env is None: + initial_env = os.environ print("Running Coin instruction: " + ' '.join(str(e) for e in instruction)) - result = subprocess.call(instruction) + result = subprocess.call(instruction, env=initial_env) if result != 0: print("ERROR : " + error) exit(result) def acceptCITestConfiguration(hostOS, hostOSVer, targetArch, compiler): # Disable unsupported CI configs for now - # NOTE: String must match with QT CI's storagesturct thrift + # NOTE: String must match with QT CI's storagestruct thrift if hostOSVer in ["WinRT_10"]: print("Disabled " + hostOSVer + " from Coin configuration") return False @@ -1124,3 +1145,13 @@ def acceptCITestConfiguration(hostOS, hostOSVer, targetArch, compiler): print("Disabled " + compiler + " to " + targetArch + " from Coin configuration") return False return True + + +def get_ci_qmake_path(ci_install_dir, ci_host_os): + qmake_path = "--qmake={}".format(ci_install_dir) + if ci_host_os == "MacOS": + return qmake_path + "/bin/qmake" + elif ci_host_os == "Windows": + return qmake_path + "\\bin\\qmake.exe" + else: + return qmake_path + "/bin/qmake" diff --git a/coin_build_instructions.py b/coin_build_instructions.py index aee0bf265..75f9feb66 100644 --- a/coin_build_instructions.py +++ b/coin_build_instructions.py @@ -36,14 +36,16 @@ ## $QT_END_LICENSE$ ## ############################################################################# -from build_scripts.utils import has_option -from build_scripts.utils import option_value +from build_scripts.options import has_option +from build_scripts.options import option_value from build_scripts.utils import install_pip_dependencies +from build_scripts.utils import install_pip_wheel_package from build_scripts.utils import get_qtci_virtualEnv from build_scripts.utils import run_instruction from build_scripts.utils import rmtree from build_scripts.utils import get_python_dict from build_scripts.utils import acceptCITestConfiguration +from build_scripts.utils import get_ci_qmake_path import os # Values must match COIN thrift @@ -98,19 +100,17 @@ def call_setup(python_ver): _pExe, _env, env_pip, env_python = get_qtci_virtualEnv(python_ver, CI_HOST_OS, CI_HOST_ARCH, CI_TARGET_ARCH) rmtree(_env, True) run_instruction(["virtualenv", "-p", _pExe, _env], "Failed to create virtualenv") - install_pip_dependencies(env_pip, ["six", "wheel"]) - cmd = [env_python, "setup.py"] + + install_pip_dependencies(env_pip, ["six", "setuptools"]) + install_pip_wheel_package(env_pip) + + cmd = [env_python, "-u", "setup.py"] if CI_RELEASE_CONF: cmd += ["bdist_wheel", "--standalone"] else: cmd += ["build"] - if CI_HOST_OS == "MacOS": - cmd += ["--qmake=" + CI_ENV_INSTALL_DIR + "/bin/qmake"] - elif CI_HOST_OS == "Windows": - - cmd += ["--qmake=" + CI_ENV_INSTALL_DIR + "\\bin\\qmake.exe"] - else: - cmd += ["--qmake=" + CI_ENV_INSTALL_DIR + "/bin/qmake"] + qmake_path = get_ci_qmake_path(CI_ENV_INSTALL_DIR, CI_HOST_OS) + cmd.append(qmake_path) cmd += ["--build-tests", "--jobs=4", "--verbose-build"] @@ -121,7 +121,23 @@ def call_setup(python_ver): cmd += ["--package-timestamp=" + CI_INTEGRATION_ID] - run_instruction(cmd, "Failed to run setup.py") + env = os.environ + if CI_HOST_OS == "MacOS": + # On Python 3, setuptools.dist.handle_display_options does some + # weird sys.stdout.detach-ing if the stdout encoding is + # different from utf-8. This causes issues when running + # subprocess.call() because that access the original stdout + # object stored in sys.__stdout__ which was detached, and + # results in an exception being thrown. + # The Coin macOS locale by default is US-ASCII, and that + # triggers the above issue. Set the encoding to UTF-8 which + # makes sure to skip over the detach-ing code. + # Relevant links to the issue: + # https://bugs.python.org/issue15216 + # https://bitbucket.org/tarek/distribute/issues/334/fix-for-311-breaks-packages-that-use + # https://github.com/pypa/virtualenv/issues/359 + env['LC_CTYPE'] = 'UTF-8' + run_instruction(cmd, "Failed to run setup.py", initial_env=env) def run_build_instructions(): if not acceptCITestConfiguration(CI_HOST_OS, CI_HOST_OS_VER, CI_TARGET_ARCH, CI_COMPILER): diff --git a/coin_test_instructions.py b/coin_test_instructions.py index bd65b5b7e..4121bb558 100644 --- a/coin_test_instructions.py +++ b/coin_test_instructions.py @@ -36,13 +36,15 @@ ## $QT_END_LICENSE$ ## ############################################################################# -from build_scripts.utils import has_option -from build_scripts.utils import option_value +from build_scripts.options import has_option +from build_scripts.options import option_value from build_scripts.utils import install_pip_dependencies +from build_scripts.utils import install_pip_wheel_package from build_scripts.utils import get_qtci_virtualEnv from build_scripts.utils import run_instruction from build_scripts.utils import rmtree from build_scripts.utils import acceptCITestConfiguration +from build_scripts.utils import get_ci_qmake_path import os # Values must match COIN thrift @@ -66,12 +68,21 @@ def call_testrunner(python_ver, buildnro): _pExe, _env, env_pip, env_python = get_qtci_virtualEnv(python_ver, CI_HOST_OS, CI_HOST_ARCH, CI_TARGET_ARCH) rmtree(_env, True) run_instruction(["virtualenv", "-p", _pExe, _env], "Failed to create virtualenv") - install_pip_dependencies(env_pip, ["six", "wheel"]) + install_pip_dependencies(env_pip, ["six", "setuptools"]) + install_pip_wheel_package(env_pip) cmd = [env_python, "testrunner.py", "test", "--blacklist", "build_history/blacklist.txt", "--buildno=" + buildnro] run_instruction(cmd, "Failed to run testrunner.py") + qmake_path = get_ci_qmake_path(CI_ENV_INSTALL_DIR, CI_HOST_OS) + + # Try to install built wheels, and build some buildable examples. + if CI_RELEASE_CONF: + wheel_tester_path = os.path.join("testing", "wheel_tester.py") + cmd = [env_python, wheel_tester_path, qmake_path] + run_instruction(cmd, "Error while running wheel_tester.py") + def run_test_instructions(): if not acceptCITestConfiguration(CI_HOST_OS, CI_HOST_OS_VER, CI_TARGET_ARCH, CI_COMPILER): exit() diff --git a/examples/samplebinding/CMakeLists.txt b/examples/samplebinding/CMakeLists.txt index 03ab85754..3852ed36f 100644 --- a/examples/samplebinding/CMakeLists.txt +++ b/examples/samplebinding/CMakeLists.txt @@ -40,7 +40,11 @@ set(generated_sources # ================================== Shiboken detection ====================================== - +# Use provided python interpreter if given. +if(NOT python_interpreter) + find_program(python_interpreter "python") +endif() +message(STATUS "Using python interpreter: ${python_interpreter}") # Macro to get various pyside / python include / link flags and paths. # Uses the not entirely supported utils/pyside2_config.py file. @@ -52,7 +56,8 @@ macro(pyside2_config option output_var) endif() execute_process( - COMMAND python "${CMAKE_SOURCE_DIR}/../utils/pyside2_config.py" ${option} + COMMAND ${python_interpreter} "${CMAKE_SOURCE_DIR}/../utils/pyside2_config.py" + ${option} OUTPUT_VARIABLE ${output_var} OUTPUT_STRIP_TRAILING_WHITESPACE) @@ -64,14 +69,15 @@ macro(pyside2_config option output_var) endif() endmacro() -# Query for the shiboken path, Python path, include paths and linker flags. -pyside2_config(--pyside2 pyside2_path) -pyside2_config(--python-include python_include_dir) -pyside2_config(--shiboken-include shiboken_include_dir 1) -pyside2_config(--shiboken-shared-libraries-cmake shiboken_shared_libraries 0) -pyside2_config(--python-link-cmake python_linking_data 0) +# Query for the shiboken generator path, Python path, include paths and linker flags. +pyside2_config(--shiboken2-module-path shiboken2_module_path) +pyside2_config(--shiboken2-generator-path shiboken2_generator_path) +pyside2_config(--python-include-path python_include_dir) +pyside2_config(--shiboken2-generator-include-path shiboken_include_dir 1) +pyside2_config(--shiboken2-module-shared-libraries-cmake shiboken_shared_libraries 0) +pyside2_config(--python-link-flags-cmake python_linking_data 0) -set(shiboken_path "${pyside2_path}/shiboken2${CMAKE_EXECUTABLE_SUFFIX}") +set(shiboken_path "${shiboken2_generator_path}/shiboken2${CMAKE_EXECUTABLE_SUFFIX}") if(NOT EXISTS ${shiboken_path}) message(FATAL_ERROR "Shiboken executable not found at path: ${shiboken_path}") endif() @@ -87,7 +93,7 @@ endif() # Enable rpaths so that the built shared libraries find their dependencies. set(CMAKE_SKIP_BUILD_RPATH FALSE) set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) -set(CMAKE_INSTALL_RPATH ${pyside2_path} ${CMAKE_CURRENT_SOURCE_DIR}) +set(CMAKE_INSTALL_RPATH ${shiboken2_module_path} ${CMAKE_CURRENT_SOURCE_DIR}) set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) # ============================================================================================= # !!! End of dubious section. diff --git a/examples/scriptableapplication/CMakeLists.txt b/examples/scriptableapplication/CMakeLists.txt index 4119b6756..215d08961 100644 --- a/examples/scriptableapplication/CMakeLists.txt +++ b/examples/scriptableapplication/CMakeLists.txt @@ -14,6 +14,12 @@ set(CMAKE_CXX_STANDARD 11) # Find required Qt packages. find_package(Qt5 5.9 REQUIRED COMPONENTS Core Gui Widgets) +# Use provided python interpreter if given. +if(NOT python_interpreter) + find_program(python_interpreter "python") +endif() +message(STATUS "Using python interpreter: ${python_interpreter}") + # Macro to get various pyside / python include / link flags. macro(pyside2_config option output_var) if(${ARGC} GREATER 2) @@ -23,7 +29,8 @@ macro(pyside2_config option output_var) endif() execute_process( - COMMAND python "${CMAKE_SOURCE_DIR}/../utils/pyside2_config.py" ${option} + COMMAND ${python_interpreter} "${CMAKE_SOURCE_DIR}/../utils/pyside2_config.py" + ${option} OUTPUT_VARIABLE ${output_var} OUTPUT_STRIP_TRAILING_WHITESPACE) @@ -35,14 +42,20 @@ macro(pyside2_config option output_var) endif() endmacro() -# Query for the PySide2 path, Python path, include paths and linker flags. -pyside2_config(--pyside2 PYSIDE2_PATH) -pyside2_config(--python-include PYTHON_INCLUDE_DIR) -pyside2_config(--pyside2-include PYSIDE2_INCLUDE_DIR 1) +# Query for the shiboken2-generator path, PySide2 path, Python path, include paths and linker flags. +pyside2_config(--shiboken2-module-path SHIBOKEN2_MODULE_PATH) +pyside2_config(--shiboken2-generator-path SHIBOKEN2_GENERATOR_PATH) +pyside2_config(--pyside2-path PYSIDE2_PATH) + +pyside2_config(--python-include-path PYTHON_INCLUDE_DIR) +pyside2_config(--shiboken2-generator-include-path SHIBOKEN2_GENERATOR_INCLUDE_DIR 1) +pyside2_config(--pyside2-include-path PYSIDE2_INCLUDE_DIR 1) + +pyside2_config(--python-link-flags-cmake PYTHON_LINKING_DATA 0) +pyside2_config(--shiboken2-module-shared-libraries-cmake SHIBOKEN2_MODULE_SHARED_LIBRARIES 0) pyside2_config(--pyside2-shared-libraries-cmake PYSIDE2_SHARED_LIBRARIES 0) -pyside2_config(--python-link-cmake PYTHON_LINKING_DATA 0) -set(SHIBOKEN_PATH "${PYSIDE2_PATH}/shiboken2${CMAKE_EXECUTABLE_SUFFIX}") +set(SHIBOKEN_PATH "${SHIBOKEN2_GENERATOR_PATH}/shiboken2${CMAKE_EXECUTABLE_SUFFIX}") if(NOT EXISTS ${SHIBOKEN_PATH}) message(FATAL_ERROR "Shiboken executable not found at path: ${SHIBOKEN_PATH}") @@ -122,7 +135,7 @@ endforeach() # Enable rpaths so that the example can be executed from the build dir. set(CMAKE_SKIP_BUILD_RPATH FALSE) set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) -set(CMAKE_INSTALL_RPATH ${PYSIDE2_PATH}) +set(CMAKE_INSTALL_RPATH ${PYSIDE2_PATH} ${SHIBOKEN2_MODULE_PATH}) set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) # ============================================================================================= # !!! End of dubious section. @@ -139,11 +152,13 @@ target_sources(${PROJECT_NAME} PUBLIC ${SOURCES}) # Apply relevant include and link flags. target_include_directories(${PROJECT_NAME} PRIVATE ${PYTHON_INCLUDE_DIR}) +target_include_directories(${PROJECT_NAME} PRIVATE ${SHIBOKEN2_GENERATOR_INCLUDE_DIR}) target_include_directories(${PROJECT_NAME} PRIVATE ${PYSIDE2_INCLUDE_DIR}) target_include_directories(${PROJECT_NAME} PRIVATE ${PYSIDE2_ADDITIONAL_INCLUDES}) target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_SOURCE_DIR}) target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::Widgets) +target_link_libraries(${PROJECT_NAME} PRIVATE ${SHIBOKEN2_MODULE_SHARED_LIBRARIES}) target_link_libraries(${PROJECT_NAME} PRIVATE ${PYSIDE2_SHARED_LIBRARIES}) # Find and link to the python library. @@ -176,7 +191,8 @@ if(WIN32) # Add custom target to hard link PySide2 shared libraries (just like in qmake example), so you # don't have to set PATH manually to point to the PySide2 package. - foreach(LIBRARY_PATH ${PYSIDE2_SHARED_LIBRARIES}) + set(shared_libraries ${SHIBOKEN2_MODULE_SHARED_LIBRARIES} ${PYSIDE2_SHARED_LIBRARIES}) + foreach(LIBRARY_PATH ${shared_libraries}) string(REGEX REPLACE ".lib$" ".dll" LIBRARY_PATH ${LIBRARY_PATH}) get_filename_component(BASE_NAME ${LIBRARY_PATH} NAME) file(TO_NATIVE_PATH ${LIBRARY_PATH} SOURCE_PATH) diff --git a/examples/scriptableapplication/pyside2.pri b/examples/scriptableapplication/pyside2.pri index 17be4392f..2da3bc880 100644 --- a/examples/scriptableapplication/pyside2.pri +++ b/examples/scriptableapplication/pyside2.pri @@ -1,30 +1,52 @@ PYSIDE_CONFIG = $$PWD/../utils/pyside2_config.py -PYSIDE2 = $$system(python $$PYSIDE_CONFIG --pyside2) +# Use provided python interpreter if given. +isEmpty(python_interpreter) { + python_interpreter = python +} +message(Using python interpreter: $$python_interpreter) + +SHIBOKEN2_GENERATOR = $$system($$python_interpreter $$PYSIDE_CONFIG --shiboken2-generator-path) +isEmpty(SHIBOKEN2_GENERATOR): error(Unable to locate the shiboken2-generator package location) + +SHIBOKEN2_MODULE = $$system($$python_interpreter $$PYSIDE_CONFIG --shiboken2-module-path) +isEmpty(SHIBOKEN2_MODULE): error(Unable to locate the shiboken2 package location) + +PYSIDE2 = $$system($$python_interpreter $$PYSIDE_CONFIG --pyside2-path) isEmpty(PYSIDE2): error(Unable to locate the PySide2 package location) -PYTHON_INCLUDE = $$system(python $$PYSIDE_CONFIG --python-include) +PYTHON_INCLUDE = $$system($$python_interpreter $$PYSIDE_CONFIG --python-include-path) isEmpty(PYTHON_INCLUDE): error(Unable to locate the Python include headers directory) -PYTHON_LFLAGS = $$system(python $$PYSIDE_CONFIG --python-link) +PYTHON_LFLAGS = $$system($$python_interpreter $$PYSIDE_CONFIG --python-link-flags-qmake) isEmpty(PYTHON_LFLAGS): error(Unable to locate the Python library for linking) -PYSIDE2_INCLUDE = $$system(python $$PYSIDE_CONFIG --pyside2-include) +SHIBOKEN2_INCLUDE = $$system($$python_interpreter $$PYSIDE_CONFIG --shiboken2-generator-include-path) +isEmpty(SHIBOKEN2_INCLUDE): error(Unable to locate the shiboken include headers directory) + +PYSIDE2_INCLUDE = $$system($$python_interpreter $$PYSIDE_CONFIG --pyside2-include-path) isEmpty(PYSIDE2_INCLUDE): error(Unable to locate the PySide2 include headers directory) -PYSIDE2_LFLAGS = $$system(python $$PYSIDE_CONFIG --pyside2-link) +SHIBOKEN2_LFLAGS = $$system($$python_interpreter $$PYSIDE_CONFIG --shiboken2-module-qmake-lflags) +isEmpty(SHIBOKEN2_LFLAGS): error(Unable to locate the shiboken libraries for linking) + +PYSIDE2_LFLAGS = $$system($$python_interpreter $$PYSIDE_CONFIG --pyside2-qmake-lflags) isEmpty(PYSIDE2_LFLAGS): error(Unable to locate the PySide2 libraries for linking) -PYSIDE2_SHARED_LIBRARIES = $$system(python $$PYSIDE_CONFIG --pyside2-shared-libraries) +SHIBOKEN2_SHARED_LIBRARIES = $$system($$python_interpreter $$PYSIDE_CONFIG --shiboken2-module-shared-libraries-qmake) +isEmpty(SHIBOKEN2_SHARED_LIBRARIES): error(Unable to locate the used shiboken2 module shared libraries) + +PYSIDE2_SHARED_LIBRARIES = $$system($$python_interpreter $$PYSIDE_CONFIG --pyside2-shared-libraries-qmake) isEmpty(PYSIDE2_SHARED_LIBRARIES): error(Unable to locate the used PySide2 shared libraries) -INCLUDEPATH += "$$PYTHON_INCLUDE" $$PYSIDE2_INCLUDE -LIBS += $$PYTHON_LFLAGS $$PYSIDE2_LFLAGS +INCLUDEPATH += "$$PYTHON_INCLUDE" $$PYSIDE2_INCLUDE $$SHIBOKEN2_INCLUDE +LIBS += $$PYTHON_LFLAGS $$PYSIDE2_LFLAGS $$SHIBOKEN2_LFLAGS !build_pass:message(INCLUDEPATH is $$INCLUDEPATH) !build_pass:message(LIBS are $$LIBS) !build_pass:message(Using $$PYSIDE2) !win32 { - QMAKE_RPATHDIR += $$PYSIDE2 + !build_pass:message(RPATH will include $$PYSIDE2 and $$SHIBOKEN2_MODULE) + QMAKE_RPATHDIR += $$PYSIDE2 $$SHIBOKEN2_MODULE } diff --git a/examples/scriptableapplication/scriptableapplication.pro b/examples/scriptableapplication/scriptableapplication.pro index 8a09b0abf..8ebab9476 100644 --- a/examples/scriptableapplication/scriptableapplication.pro +++ b/examples/scriptableapplication/scriptableapplication.pro @@ -23,7 +23,7 @@ SHIBOKEN_OPTIONS = --generator-set=shiboken --enable-parent-ctor-heuristic \ win32:SHIBOKEN_OPTIONS += --avoid-protected-hack # Prepare the shiboken tool -QT_TOOL.shiboken.binary = $$system_path($$PYSIDE2/shiboken2) +QT_TOOL.shiboken.binary = $$system_path($$SHIBOKEN2_GENERATOR/shiboken2) qtPrepareTool(SHIBOKEN, shiboken) # Shiboken run that adds the module wrapper to GENERATED_SOURCES @@ -61,7 +61,7 @@ win32 { hard_link_libraries.CONFIG = no_link target_predeps explicit_dependencies hard_link_libraries.output = $$out_dir/${QMAKE_FILE_BASE}${QMAKE_FILE_EXT} hard_link_libraries.commands = mklink /H $$shell_path($$out_dir/${QMAKE_FILE_BASE}${QMAKE_FILE_EXT}) $$shell_path(${QMAKE_FILE_IN}) - hard_link_libraries.input = PYSIDE2_SHARED_LIBRARIES + hard_link_libraries.input = PYSIDE2_SHARED_LIBRARIES SHIBOKEN2_SHARED_LIBRARIES } QMAKE_EXTRA_COMPILERS += shiboken module_wrapper_dummy_command diff --git a/examples/utils/pyside2_config.py b/examples/utils/pyside2_config.py index 298d40d5b..c62b38cad 100644 --- a/examples/utils/pyside2_config.py +++ b/examples/utils/pyside2_config.py @@ -38,28 +38,106 @@ ## ############################################################################# -import os, glob, re, sys, imp +import os, glob, re, sys from distutils import sysconfig +generic_error = (' Did you forget to activate your virtualenv? Or perhaps' + ' you forgot to build / install PySide2 into your currently active Python' + ' environment?') +pyside2_error = 'Unable to locate PySide2.' + generic_error +shiboken2_module_error = 'Unable to locate shiboken2-module.' + generic_error +shiboken2_generator_error = 'Unable to locate shiboken2-generator.' + generic_error +pyside2_libs_error = 'Unable to locate the PySide2 shared libraries.' + generic_error +python_link_error = 'Unable to locate the Python library for linking.' +python_include_error = 'Unable to locate the Python include headers directory.' + +options = [] + +# option, function, error, description +options.append(("--shiboken2-module-path", + lambda: find_shiboken2_module(), + shiboken2_module_error, + "Print shiboken2 module location")) +options.append(("--shiboken2-generator-path", + lambda: find_shiboken2_generator(), + shiboken2_generator_error, + "Print shiboken2 generator location")) +options.append(("--pyside2-path", lambda: find_pyside2(), pyside2_error, + "Print PySide2 location")) + +options.append(("--python-include-path", + lambda: get_python_include_path(), + python_include_error, + "Print Python include path")) +options.append(("--shiboken2-generator-include-path", + lambda: get_package_include_path(Package.shiboken2_generator), + pyside2_error, + "Print shiboken2 generator include paths")) +options.append(("--pyside2-include-path", + lambda: get_package_include_path(Package.pyside2), + pyside2_error, + "Print PySide2 include paths")) + +options.append(("--python-link-flags-qmake", lambda: python_link_flags_qmake(), python_link_error, + "Print python link flags for qmake")) +options.append(("--python-link-flags-cmake", lambda: python_link_flags_cmake(), python_link_error, + "Print python link flags for cmake")) + +options.append(("--shiboken2-module-qmake-lflags", + lambda: get_package_qmake_lflags(Package.shiboken2_module), pyside2_error, + "Print shiboken2 shared library link flags for qmake")) +options.append(("--pyside2-qmake-lflags", + lambda: get_package_qmake_lflags(Package.pyside2), pyside2_error, + "Print PySide2 shared library link flags for qmake")) + +options.append(("--shiboken2-module-shared-libraries-qmake", + lambda: get_shared_libraries_qmake(Package.shiboken2_module), pyside2_libs_error, + "Print paths of shiboken2 shared libraries (.so's, .dylib's, .dll's) for qmake")) +options.append(("--shiboken2-module-shared-libraries-cmake", + lambda: get_shared_libraries_cmake(Package.shiboken2_module), pyside2_libs_error, + "Print paths of shiboken2 shared libraries (.so's, .dylib's, .dll's) for cmake")) + +options.append(("--pyside2-shared-libraries-qmake", + lambda: get_shared_libraries_qmake(Package.pyside2), pyside2_libs_error, + "Print paths of PySide2 shared libraries (.so's, .dylib's, .dll's) for qmake")) +options.append(("--pyside2-shared-libraries-cmake", + lambda: get_shared_libraries_cmake(Package.pyside2), pyside2_libs_error, + "Print paths of PySide2 shared libraries (.so's, .dylib's, .dll's) for cmake")) + +options_usage = '' +for i, (flag, _, _, description) in enumerate(options): + options_usage += ' {:<45} {}'.format(flag, description) + if i < len(options) - 1: + options_usage += '\n' + usage = """ -Utility to determine include/link options of PySide2 and Python for qmake +Utility to determine include/link options of shiboken2/PySide2 and Python for qmake/CMake projects +that would like to embed or build custom shiboken2/PySide2 bindings. Usage: pyside2_config.py [option] Options: - --python-include Print Python include path - --python-link Print Python link flags - --pyside2 Print PySide2 location - --pyside2-include Print PySide2 include paths - --pyside2-link Print PySide2 link flags - --pyside2-shared-libraries Print paths of PySide2 shared libraries (.so's, .dylib's, .dll's) - -a Print all - --help/-h Print this help -""" - -def cleanPath(path): +{} + -a Print all options and their values + --help/-h Print this help +""".format(options_usage) + +option = sys.argv[1] if len(sys.argv) == 2 else '-a' +if option == '-h' or option == '--help': + print(usage) + sys.exit(0) + + +class Package(object): + shiboken2_module = 1 + shiboken2_generator = 2 + pyside2 = 3 + + +def clean_path(path): return path if sys.platform != 'win32' else path.replace('\\', '/') -def sharedLibrarySuffix(): + +def shared_library_suffix(): if sys.platform == 'win32': return 'lib' elif sys.platform == 'darwin': @@ -68,21 +146,41 @@ def sharedLibrarySuffix(): else: return 'so.*' -def sharedLibraryGlobPattern(): - glob = '*.' + sharedLibrarySuffix() + +def import_suffixes(): + if (sys.version_info >= (3, 4)): + import importlib + return importlib.machinery.EXTENSION_SUFFIXES + else: + import imp + result = [] + for t in imp.get_suffixes(): + result.append(t[0]) + return result + + +def is_debug(): + debug_suffix = '_d.pyd' if sys.platform == 'win32' else '_d.so' + return any([s.endswith(debug_suffix) for s in import_suffixes()]) + + +def shared_library_glob_pattern(): + glob = '*.' + shared_library_suffix() return glob if sys.platform == 'win32' else 'lib' + glob -def filterPySide2SharedLibraries(list, only_shiboken=False): - def predicate(item): - basename = os.path.basename(item) - if 'shiboken' in basename or ('pyside2' in basename and not only_shiboken): + +def filter_shared_libraries(libs_list): + def predicate(lib_name): + basename = os.path.basename(lib_name) + if 'shiboken' in basename or 'pyside2' in basename: return True return False - result = [item for item in list if predicate(item)] + result = [lib for lib in libs_list if predicate(lib)] return result + # Return qmake link option for a library file name -def linkOption(lib): +def link_option(lib): # On Linux: # Since we cannot include symlinks with wheel packages # we are using an absolute path for the libpyside and libshiboken @@ -97,24 +195,50 @@ def linkOption(lib): link += os.path.splitext(baseName)[0] return link -# Locate PySide2 via package path -def findPySide2(): + +# Locate PySide2 via sys.path package path. +def find_pyside2(): + return find_package_path("PySide2") + + +def find_shiboken2_module(): + return find_package_path("shiboken2") + + +def find_shiboken2_generator(): + return find_package_path("shiboken2_generator") + + +def find_package(which_package): + if which_package == Package.shiboken2_module: + return find_shiboken2_module() + if which_package == Package.shiboken2_generator: + return find_shiboken2_generator() + if which_package == Package.pyside2: + return find_pyside2() + return None + + +def find_package_path(dir_name): for p in sys.path: if 'site-' in p: - pyside2 = os.path.join(p, 'PySide2') - if os.path.exists(pyside2): - return cleanPath(os.path.realpath(pyside2)) + package = os.path.join(p, dir_name) + if os.path.exists(package): + return clean_path(os.path.realpath(package)) return None + # Return version as "3.5" -def pythonVersion(): +def python_version(): return str(sys.version_info[0]) + '.' + str(sys.version_info[1]) -def pythonInclude(): + +def get_python_include_path(): return sysconfig.get_python_inc() -def pythonLinkQmake(): - flags = pythonLinkData() + +def python_link_flags_qmake(): + flags = python_link_data() if sys.platform == 'win32': libdir = flags['libdir'] # This will add the "~1" shortcut for directories that @@ -131,25 +255,27 @@ def pythonLinkQmake(): # Linux and anything else return '-L{} -l{}'.format(flags['libdir'], flags['lib']) -def pythonLinkCmake(): - flags = pythonLinkData() + +def python_link_flags_cmake(): + flags = python_link_data() libdir = flags['libdir'] lib = re.sub(r'.dll$', '.lib', flags['lib']) return '{};{}'.format(libdir, lib) -def pythonLinkData(): + +def python_link_data(): # @TODO Fix to work with static builds of Python libdir = sysconfig.get_config_var('LIBDIR') if libdir is None: libdir = os.path.abspath(os.path.join( sysconfig.get_config_var('LIBDEST'), "..", "libs")) - version = pythonVersion() + version = python_version() version_no_dots = version.replace('.', '') flags = {} flags['libdir'] = libdir if sys.platform == 'win32': - suffix = '_d' if any([tup[0].endswith('_d.pyd') for tup in imp.get_suffixes()]) else '' + suffix = '_d' if is_debug() else '' flags['lib'] = 'python{}{}'.format(version_no_dots, suffix) elif sys.platform == 'darwin': @@ -158,42 +284,44 @@ def pythonLinkData(): # Linux and anything else else: if sys.version_info[0] < 3: - suffix = '_d' if any([tup[0].endswith('_d.so') for tup in imp.get_suffixes()]) else '' + suffix = '_d' if is_debug() else '' flags['lib'] = 'python{}{}'.format(version, suffix) else: flags['lib'] = 'python{}{}'.format(version, sys.abiflags) return flags -def pyside2Include(only_shiboken=False): - pySide2 = findPySide2() - if pySide2 is None: + +def get_package_include_path(which_package): + package_path = find_package(which_package) + if package_path is None: return None - includes = "{0}/include/shiboken2".format(pySide2) - if not only_shiboken: - includes = includes + " {0}/include/PySide2".format(pySide2) + includes = "{0}/include".format(package_path) return includes -def pyside2Link(): - pySide2 = findPySide2() - if pySide2 is None: + +def get_package_qmake_lflags(which_package): + package_path = find_package(which_package) + if package_path is None: return None - link = "-L{}".format(pySide2) - glob_result = glob.glob(os.path.join(pySide2, sharedLibraryGlobPattern())) - for lib in filterPySide2SharedLibraries(glob_result): + + link = "-L{}".format(package_path) + glob_result = glob.glob(os.path.join(package_path, shared_library_glob_pattern())) + for lib in filter_shared_libraries(glob_result): link += ' ' - link += linkOption(lib) + link += link_option(lib) return link -def pyside2SharedLibrariesData(only_shiboken=False): - pySide2 = findPySide2() - if pySide2 is None: + +def get_shared_libraries_data(which_package): + package_path = find_package(which_package) + if package_path is None: return None - glob_result = glob.glob(os.path.join(pySide2, sharedLibraryGlobPattern())) - filtered_libs = filterPySide2SharedLibraries(glob_result, only_shiboken) + glob_result = glob.glob(os.path.join(package_path, shared_library_glob_pattern())) + filtered_libs = filter_shared_libraries(glob_result) libs = [] if sys.platform == 'win32': for lib in filtered_libs: @@ -203,8 +331,9 @@ def pyside2SharedLibrariesData(only_shiboken=False): libs.append(lib) return libs -def pyside2SharedLibraries(): - libs = pyside2SharedLibrariesData() + +def get_shared_libraries_qmake(which_package): + libs = get_shared_libraries_data(which_package) if libs is None: return None @@ -223,80 +352,21 @@ def pyside2SharedLibraries(): libs_string += lib + ' ' return libs_string -def pyside2SharedLibrariesCmake(only_shiboken=False): - libs = pyside2SharedLibrariesData(only_shiboken) + +def get_shared_libraries_cmake(which_package): + libs = get_shared_libraries_data(which_package) result = ';'.join(libs) return result -option = sys.argv[1] if len(sys.argv) == 2 else '-a' -if option == '-h' or option == '--help': - print(usage) - sys.exit(0) -generic_error = (' Did you forget to activate your virtualenv? Or perhaps' - ' you forgot to build / install PySide2 into your currently active Python' - ' environment?') -pyside2_error = 'Unable to locate PySide2.' + generic_error -pyside2_libs_error = 'Unable to locate the PySide2 shared libraries.' + generic_error -python_link_error = 'Unable to locate the Python library for linking.' +print_all = option == "-a" +for argument, handler, error, _ in options: + if option == argument or print_all: + handler_result = handler() + if handler_result is None: + sys.exit(error) -if option == '--pyside2' or option == '-a': - pySide2 = findPySide2() - if pySide2 is None: - sys.exit(pyside2_error) - print(pySide2) - -if option == '--pyside2-link' or option == '-a': - l = pyside2Link() - if l is None: - sys.exit(pyside2_error) - - print(l) - -if option == '--shiboken-include' or option == '-a': - i = pyside2Include(only_shiboken=True) - if i is None: - sys.exit(pyside2_error) - print(i) - -if option == '--pyside2-include' or option == '-a': - i = pyside2Include() - if i is None: - sys.exit(pyside2_error) - print(i) - -if option == '--python-include' or option == '-a': - i = pythonInclude() - if i is None: - sys.exit('Unable to locate the Python include headers directory.') - print(i) - -if option == '--python-link' or option == '-a': - l = pythonLinkQmake() - if l is None: - sys.exit(python_link_error) - print(l) - -if option == '--python-link-cmake' or option == '-a': - l = pythonLinkCmake() - if l is None: - sys.exit(python_link_error) - print(l) - -if option == '--pyside2-shared-libraries' or option == '-a': - l = pyside2SharedLibraries() - if l is None: - sys.exit(pyside2_libs_error) - print(l) - -if option == '--pyside2-shared-libraries-cmake' or option == '-a': - l = pyside2SharedLibrariesCmake() - if l is None: - sys.exit(pyside2_libs_error) - print(l) - -if option == '--shiboken-shared-libraries-cmake' or option == '-a': - l = pyside2SharedLibrariesCmake(only_shiboken=True) - if l is None: - sys.exit(pyside2_libs_error) - print(l) + line = handler_result + if print_all: + line = "{:<40}: ".format(argument) + line + print(line) diff --git a/keyword-errors.lst b/keyword-errors.lst new file mode 100644 index 000000000..af8c581a5 --- /dev/null +++ b/keyword-errors.lst @@ -0,0 +1,43 @@ +KEYWORD {'multi': None, 'funcname': 'PySide2.QtCore.QAbstractItemModel.changePersistentIndex', 'arglist': 'from:PySide2.QtCore.QModelIndex,to:PySide2.QtCore.QModelIndex', 'returntype': None} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtCore.QAbstractItemModel.changePersistentIndexList', 'arglist': 'from:QModelIndexList,to:QModelIndexList', 'returntype': None} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtCore.QByteArray.indexOf', 'arglist': 'a:PySide2.QtCore.QByteArray,from:int=0', 'returntype': 'int'} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtCore.QByteArray.lastIndexOf', 'arglist': 'a:PySide2.QtCore.QByteArray,from:int=-1', 'returntype': 'int'} +KEYWORD {'multi': '1', 'funcname': 'PySide2.QtCore.QByteArrayMatcher.indexIn', 'arglist': 'ba:PySide2.QtCore.QByteArray,from:int=0', 'returntype': 'int'} +KEYWORD {'multi': '0', 'funcname': 'PySide2.QtCore.QByteArrayMatcher.indexIn', 'arglist': 'str:str,len:int,from:int=0', 'returntype': 'int'} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtCore.QItemSelection.indexOf', 'arglist': 't:PySide2.QtCore.QItemSelectionRange,from:int=0', 'returntype': 'int'} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtCore.QItemSelection.lastIndexOf', 'arglist': 't:PySide2.QtCore.QItemSelectionRange,from:int=-1', 'returntype': 'int'} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtCore.QItemSelection.move', 'arglist': 'from:int,to:int', 'returntype': None} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtCore.QTextCodec.convertToUnicode', 'arglist': 'in:str,length:int,state:PySide2.QtCore.QTextCodec.ConverterState', 'returntype': 'QString'} +KEYWORD {'multi': '0', 'funcname': 'PySide2.QtCore.QTextCodec.toUnicode', 'arglist': 'in:str,length:int,state:PySide2.QtCore.QTextCodec.ConverterState=nullptr', 'returntype': 'QString'} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtCore.QVariantAnimation.interpolated', 'arglist': 'from:QVariant,to:QVariant,progress:double', 'returntype': 'QVariant'} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtCore.QXmlStreamAttributes.indexOf', 'arglist': 't:PySide2.QtCore.QXmlStreamAttribute,from:int=0', 'returntype': 'int'} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtCore.QXmlStreamAttributes.lastIndexOf', 'arglist': 't:PySide2.QtCore.QXmlStreamAttribute,from:int=-1', 'returntype': 'int'} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtCore.QXmlStreamAttributes.move', 'arglist': 'from:int,to:int', 'returntype': None} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtGui.QAbstractTextDocumentLayout.documentChanged', 'arglist': 'from:int,charsRemoved:int,charsAdded:int', 'returntype': None} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtGui.QIconEngine.read', 'arglist': 'in:PySide2.QtCore.QDataStream', 'returntype': 'bool'} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtGui.QKeySequence.__lshift__', 'arglist': 'in:PySide2.QtCore.QDataStream', 'returntype': 'PySide2.QtCore.QDataStream'} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtGui.QPolygon.indexOf', 'arglist': 't:PySide2.QtCore.QPoint,from:int=0', 'returntype': 'int'} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtGui.QPolygon.lastIndexOf', 'arglist': 't:PySide2.QtCore.QPoint,from:int=-1', 'returntype': 'int'} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtGui.QPolygon.move', 'arglist': 'from:int,to:int', 'returntype': None} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtGui.QPolygonF.indexOf', 'arglist': 't:PySide2.QtCore.QPointF,from:int=0', 'returntype': 'int'} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtGui.QPolygonF.lastIndexOf', 'arglist': 't:PySide2.QtCore.QPointF,from:int=-1', 'returntype': 'int'} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtGui.QPolygonF.move', 'arglist': 'from:int,to:int', 'returntype': None} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtGui.QQuaternion.rotationTo', 'arglist': 'from:PySide2.QtGui.QVector3D,to:PySide2.QtGui.QVector3D', 'returntype': 'PySide2.QtGui.QQuaternion'} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtGui.QStandardItem.read', 'arglist': 'in:PySide2.QtCore.QDataStream', 'returntype': None} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtGui.QStandardItem.__rshift__', 'arglist': 'in:PySide2.QtCore.QDataStream', 'returntype': 'PySide2.QtCore.QDataStream'} +KEYWORD {'multi': '4', 'funcname': 'PySide2.QtGui.QTextDocument.find', 'arglist': 'expr:PySide2.QtCore.QRegExp,from:int=0,options:PySide2.QtGui.QTextDocument.FindFlags=QTextDocument.FindFlags()', 'returntype': 'PySide2.QtGui.QTextCursor'} +KEYWORD {'multi': '2', 'funcname': 'PySide2.QtGui.QTextDocument.find', 'arglist': 'expr:PySide2.QtCore.QRegularExpression,from:int=0,options:PySide2.QtGui.QTextDocument.FindFlags=QTextDocument.FindFlags()', 'returntype': 'PySide2.QtGui.QTextCursor'} +KEYWORD {'multi': '0', 'funcname': 'PySide2.QtGui.QTextDocument.find', 'arglist': 'subString:QString,from:int=0,options:PySide2.QtGui.QTextDocument.FindFlags=QTextDocument.FindFlags()', 'returntype': 'PySide2.QtGui.QTextCursor'} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtGui.QTextDocument.markContentsDirty', 'arglist': 'from:int,length:int', 'returntype': None} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtWidgets.QHeaderView.moveSection', 'arglist': 'from:int,to:int', 'returntype': None} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtWidgets.QLayout.replaceWidget', 'arglist': 'from:PySide2.QtWidgets.QWidget,to:PySide2.QtWidgets.QWidget,options:PySide2.QtCore.Qt.FindChildOptions=Qt.FindChildrenRecursively', 'returntype': 'PySide2.QtWidgets.QLayoutItem'} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtWidgets.QListWidgetItem.read', 'arglist': 'in:PySide2.QtCore.QDataStream', 'returntype': None} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtWidgets.QListWidgetItem.__rshift__', 'arglist': 'in:PySide2.QtCore.QDataStream', 'returntype': 'PySide2.QtCore.QDataStream'} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtWidgets.QPlainTextDocumentLayout.documentChanged', 'arglist': 'from:int,arg__2:int,charsAdded:int', 'returntype': None} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtWidgets.QTabBar.moveTab', 'arglist': 'from:int,to:int', 'returntype': None} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtWidgets.QTableWidgetItem.read', 'arglist': 'in:PySide2.QtCore.QDataStream', 'returntype': None} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtWidgets.QTableWidgetItem.__rshift__', 'arglist': 'in:PySide2.QtCore.QDataStream', 'returntype': 'PySide2.QtCore.QDataStream'} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtWidgets.QTreeWidgetItem.read', 'arglist': 'in:PySide2.QtCore.QDataStream', 'returntype': None} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtWidgets.QTreeWidgetItem.__rshift__', 'arglist': 'in:PySide2.QtCore.QDataStream', 'returntype': 'PySide2.QtCore.QDataStream'} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtMultimedia.QAudio.convertVolume', 'arglist': 'volume:double,from:PySide2.QtMultimedia.QAudio.VolumeScale,to:PySide2.QtMultimedia.QAudio.VolumeScale', 'returntype': 'double'} +KEYWORD {'multi': None, 'funcname': 'PySide2.QtMultimedia.QMediaPlaylist.moveMedia', 'arglist': 'from:int,to:int', 'returntype': 'bool'} diff --git a/popenasync.py b/popenasync.py deleted file mode 100644 index 77faf9e0c..000000000 --- a/popenasync.py +++ /dev/null @@ -1,360 +0,0 @@ -############################################################################# -## -## Copyright (C) 2017 The Qt Company Ltd. -## Contact: https://www.qt.io/licensing/ -## -## This file is part of Qt for Python. -## -## $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$ -## -############################################################################# - -################################################################################ -""" - -Modification of http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/440554 - -""" - -#################################### IMPORTS ################################### - -import os -import subprocess -import errno -import time -import sys -import unittest -import tempfile - -def geterror (): - return sys.exc_info()[1] - -if sys.version_info >= (3,): - null_byte = '\x00'.encode('ascii') -else: - null_byte = '\x00' - -mswindows = (sys.platform == "win32") - -if mswindows: - if sys.version_info >= (3,): - # Test date should be in ascii. - def encode(s): - return s.encode('ascii', 'ignore') - - def decode(b): - return b.decode('ascii', 'ignore') - else: - # Strings only; do nothing - def encode(s): - return s - - def decode(b): - return b - - try: - import ctypes - from ctypes.wintypes import DWORD - kernel32 = ctypes.windll.kernel32 - TerminateProcess = ctypes.windll.kernel32.TerminateProcess - def WriteFile(handle, data, ol = None): - c_written = DWORD() - success = ctypes.windll.kernel32.WriteFile(handle, - ctypes.create_string_buffer(encode(data)), len(data), - ctypes.byref(c_written), ol) - return ctypes.windll.kernel32.GetLastError(), c_written.value - def ReadFile(handle, desired_bytes, ol = None): - c_read = DWORD() - buffer = ctypes.create_string_buffer(desired_bytes+1) - success = ctypes.windll.kernel32.ReadFile(handle, buffer, - desired_bytes, ctypes.byref(c_read), ol) - buffer[c_read.value] = null_byte - return ctypes.windll.kernel32.GetLastError(), decode(buffer.value) - def PeekNamedPipe(handle, desired_bytes): - c_avail = DWORD() - c_message = DWORD() - if desired_bytes > 0: - c_read = DWORD() - buffer = ctypes.create_string_buffer(desired_bytes+1) - success = ctypes.windll.kernel32.PeekNamedPipe(handle, buffer, - desired_bytes, ctypes.byref(c_read), ctypes.byref(c_avail), - ctypes.byref(c_message)) - buffer[c_read.value] = null_byte - return decode(buffer.value), c_avail.value, c_message.value - else: - success = ctypes.windll.kernel32.PeekNamedPipe(handle, None, - desired_bytes, None, ctypes.byref(c_avail), - ctypes.byref(c_message)) - return "", c_avail.value, c_message.value - - except ImportError: - from win32file import ReadFile, WriteFile - from win32pipe import PeekNamedPipe - from win32api import TerminateProcess - import msvcrt - -else: - from signal import SIGINT, SIGTERM, SIGKILL - import select - import fcntl - -################################### CONSTANTS ################################## - -PIPE = subprocess.PIPE - -################################################################################ - -class Popen(subprocess.Popen): - def __init__(self, *args, **kwargs): - subprocess.Popen.__init__(self, *args, **kwargs) - - def recv(self, maxsize=None): - return self._recv('stdout', maxsize) - - def recv_err(self, maxsize=None): - return self._recv('stderr', maxsize) - - def send_recv(self, input='', maxsize=None): - return self.send(input), self.recv(maxsize), self.recv_err(maxsize) - - def read_async(self, wait=.1, e=1, tr=5, stderr=0): - if tr < 1: - tr = 1 - x = time.time()+ wait - y = [] - r = '' - pr = self.recv - if stderr: - pr = self.recv_err - while time.time() < x or r: - r = pr() - if r is None: - if e: - raise Exception("Other end disconnected!") - else: - break - elif r: - y.append(r) - else: - time.sleep(max((x-time.time())/tr, 0)) - return ''.join(y) - - def send_all(self, data): - while len(data): - sent = self.send(data) - if sent is None: - raise Exception("Other end disconnected!") - data = buffer(data, sent) - - def get_conn_maxsize(self, which, maxsize): - if maxsize is None: - maxsize = 1024 - elif maxsize < 1: - maxsize = 1 - return getattr(self, which), maxsize - - def _close(self, which): - conn = getattr(self, which) - flags = fcntl.fcntl(conn, fcntl.F_GETFL) - if not conn.closed: - fcntl.fcntl(conn, fcntl.F_SETFL, flags & ~os.O_NONBLOCK) - assert conn.read() == '' - getattr(self, which).close() - setattr(self, which, None) - - if mswindows: - def kill(self): - # Recipes - #http://me.in-berlin.de/doc/python/faq/windows.html#how-do-i-emulate-os-kill-in-windows - #http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/347462 - - """kill function for Win32""" - TerminateProcess(int(self._handle), 0) # returns None - - def send(self, input): - if not self.stdin: - return None - - try: - x = msvcrt.get_osfhandle(self.stdin.fileno()) - (errCode, written) = WriteFile(x, input) - except ValueError: - return self._close('stdin') - except (subprocess.pywintypes.error, Exception): - if geterror()[0] in (109, errno.ESHUTDOWN): - return self._close('stdin') - raise - - return written - - def _recv(self, which, maxsize): - conn, maxsize = self.get_conn_maxsize(which, maxsize) - if conn is None: - return None - - try: - x = msvcrt.get_osfhandle(conn.fileno()) - (read, nAvail, nMessage) = PeekNamedPipe(x, 0) - if maxsize < nAvail: - nAvail = maxsize - if nAvail > 0: - (errCode, read) = ReadFile(x, nAvail, None) - except ValueError: - return self._close(which) - except (subprocess.pywintypes.error, Exception): - if geterror()[0] in (109, errno.ESHUTDOWN): - return self._close(which) - raise - - if self.universal_newlines: - # Translate newlines. For Python 3.x assume read is text. - # If bytes then another solution is needed. - read = read.replace("\r\n", "\n").replace("\r", "\n") - return read - - else: - def kill(self): - for i, sig in enumerate([SIGTERM, SIGKILL] * 2): - if i % 2 == 0: os.kill(self.pid, sig) - time.sleep((i * (i % 2) / 5.0) + 0.01) - - killed_pid, stat = os.waitpid(self.pid, os.WNOHANG) - if killed_pid != 0: return - - def send(self, input): - if not self.stdin: - return None - - if not select.select([], [self.stdin], [], 0)[1]: - return 0 - - try: - written = os.write(self.stdin.fileno(), input) - except OSError: - if geterror()[0] == errno.EPIPE: #broken pipe - return self._close('stdin') - raise - - return written - - def _recv(self, which, maxsize): - conn, maxsize = self.get_conn_maxsize(which, maxsize) - if conn is None: - return None - - flags = fcntl.fcntl(conn, fcntl.F_GETFL) - if not conn.closed: - fcntl.fcntl(conn, fcntl.F_SETFL, flags| os.O_NONBLOCK) - - try: - if not select.select([conn], [], [], 0)[0]: - return '' - - try: - r = conn.read(maxsize) - except IOError as e: - if e.errno == errno.EAGAIN: - return '' - raise - if not r: - return self._close(which) - - if self.universal_newlines: - r = r.replace("\r\n", "\n").replace("\r", "\n") - return r - finally: - if not conn.closed: - fcntl.fcntl(conn, fcntl.F_SETFL, flags) - -################################################################################ - -def proc_in_time_or_kill(cmd, time_out, wd = None, env = None): - proc = Popen ( - cmd, cwd = wd, env = env, - stdin = subprocess.PIPE, stdout = subprocess.PIPE, - stderr = subprocess.STDOUT, universal_newlines = 1 - ) - - ret_code = None - response = [] - - t = time.time() - while ret_code is None and ((time.time() -t) < time_out): - ret_code = proc.poll() - response += [proc.read_async(wait=0.1, e=0)] - - if ret_code is None: - ret_code = '"Process timed out (time_out = {} secs) '.format(time_out) - try: - proc.kill() - ret_code += 'and was successfully terminated"' - except Exception: - ret_code += ("and termination failed " - "(exception: {})".format(geterror(),)) - - return ret_code, ''.join(response) - -################################################################################ - -class AsyncTest(unittest.TestCase): - def test_proc_in_time_or_kill(self): - ret_code, response = proc_in_time_or_kill( - [sys.executable, '-c', 'while 1: pass'], time_out = 1 - ) - - self.assert_( 'rocess timed out' in ret_code ) - self.assert_( 'successfully terminated' in ret_code ) - -################################################################################ - -def _example(): - if sys.platform == 'win32': - shell, commands, tail = ('cmd', ('echo "hello"', 'echo "HELLO WORLD"'), - '\r\n') - else: - shell, commands, tail = ('sh', ('ls', 'echo HELLO WORLD'), '\n') - - a = Popen(shell, stdin=PIPE, stdout=PIPE) - sys.stdout.write(a.read_async()) - sys.stdout.write(" ") - for cmd in commands: - a.send_all(cmd + tail) - sys.stdout.write(a.read_async()) - sys.stdout.write(" ") - a.send_all('exit' + tail) - print (a.read_async(e=0)) - a.wait() - -################################################################################ - -if __name__ == '__main__': - if 1: unittest.main() - else: _example() @@ -42,12 +42,18 @@ from __future__ import print_function """ This is a distutils setup-script for the Qt for Python project -To build PySide2 simply execute: +To build both shiboken2 and PySide2 simply execute: python setup.py build or python setup.py install to build and install into your current Python installation. +The same setup.py script is used to build all the components of the +project: + - shiboken2 (the supporting Python module) + - shiboken2-generator (the bindings generation executable) + - PySide2 + - pyside2-tools Optionally, one can specify the location of qmake and cmake if it is not on the current PATH with: @@ -56,6 +62,23 @@ and --cmake=/path/to/bin/cmake respectively. +By default, all of the above is built when no special options are +passed to the script. You can use the --build-type parameter to specify +which things should be built: + --build-type=shiboken2 - build / package only the python module + --build-type=shiboken2-generator - build / package the generator + executable + --build-type=pyside2 - build / package the PySide2 bindings and + and pyside2-tools + --build-type=all - the implicit default to build all of the above + + +When building PySide2, optionally, one can specify the location of the +shiboken2 cmake config path if it is not on the current PATH with: + --shiboken-config-dir=/path/to/shiboken/cmake/config/dir +This is useful if you did a cmake installation of shiboken2 into +a custom location. + For Windows, if OpenSSL support is required, it's necessary to specify the directory path that contains the OpenSSL shared libraries "libeay32.dll" and "ssleay32.dll", for example: @@ -79,7 +102,7 @@ not specified. You can use the option `--only-package` if you want to create more binary packages (bdist_wheel, bdist_egg, ...) without rebuilding the -entire PySide2 every time: +entire project every time: e.g.: @@ -89,7 +112,7 @@ e.g.: --cmake=c:\tools\cmake\bin\cmake.exe --openssl=c:\libs\OpenSSL32bit\bin -* Then, we create a bdist_egg reusing PySide2 build with option +* Then, we create a bdist_egg reusing the PySide2 build with option `--only-package`: python setup.py bdist_egg --only-package @@ -110,7 +133,7 @@ new environment variable called PYSIDE_DISABLE_INTERNAL_QT_CONF is introduced. You should assign the integer "1" to disable the internal `qt.conf`, -or "0" (or leave empty) to keep usining the internal `qt.conf` file. +or "0" (or leave empty) to keep using the internal `qt.conf` file. DEVELOPMENT OPTIONS: @@ -230,75 +253,18 @@ this_file = os.path.abspath(this_file) if os.path.dirname(this_file): os.chdir(os.path.dirname(this_file)) -from build_scripts.main import get_package_version, get_setuptools_extension_modules -from build_scripts.main import pyside_package_dir_name -from build_scripts.main import cmd_class_dict -from build_scripts.main import README, CHANGES -from setuptools import setup, Extension +# Save the original command line arguments to pass them on to the setup +# mechanism. +original_argv = list(sys.argv) -# The __version__ variable is just for PEP compliancy, and shouldn't be -# used as a value source. +from build_scripts.main import get_package_version, check_allowed_python_version +from build_scripts.setup_runner import SetupRunner + +# The __version__ variable is just for PEP compliance, and shouldn't be +# used as a value source. Use get_package_version() instead. __version__ = get_package_version() -extension_modules = get_setuptools_extension_modules() - -setup( - name = "PySide2", - version = get_package_version(), - description = ("Python bindings for the Qt cross-platform application and " - "UI framework"), - long_description = README + "\n\n" + CHANGES, - long_description_content_type = 'text/markdown', - classifiers = [ - 'Development Status :: 5 - Production/Stable', - 'Environment :: Console', - 'Environment :: MacOS X', - 'Environment :: X11 Applications :: Qt', - 'Environment :: Win32 (MS Windows)', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)', - 'Operating System :: MacOS :: MacOS X', - 'Operating System :: POSIX', - 'Operating System :: POSIX :: Linux', - 'Operating System :: Microsoft', - 'Operating System :: Microsoft :: Windows', - 'Programming Language :: C++', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Topic :: Database', - 'Topic :: Software Development', - 'Topic :: Software Development :: Code Generators', - 'Topic :: Software Development :: Libraries :: Application Frameworks', - 'Topic :: Software Development :: User Interfaces', - 'Topic :: Software Development :: Widget Sets', - ], - keywords = 'Qt', - author = 'Qt for Python Team', - author_email = 'pyside@qt-project.org', - url = 'https://www.pyside.org', - download_url = 'https://download.qt.io/official_releases/QtForPython/', - license = 'LGPL', - packages = ['PySide2', 'pyside2uic', - 'pyside2uic.Compiler', - 'pyside2uic.port_v{}'.format(sys.version_info[0]) ], - package_dir = {'': pyside_package_dir_name}, - include_package_data = True, - zip_safe = False, - entry_points = { - 'console_scripts': [ - 'pyside2-uic = PySide2.scripts.uic:main', - ] - }, - cmdclass = cmd_class_dict, - # Add a bogus extension module (will never be built here since we - # are overriding the build command to do it using cmake) so things - # like bdist_egg will know that there are extension modules and - # will name the dist with the full platform info. - ext_modules = extension_modules, - ext_package = 'PySide2', -) +check_allowed_python_version() + +setup_runner = SetupRunner(original_argv) +setup_runner.run_setup() diff --git a/sources/cmake_helpers/helpers.cmake b/sources/cmake_helpers/helpers.cmake new file mode 100644 index 000000000..e64b8d9d3 --- /dev/null +++ b/sources/cmake_helpers/helpers.cmake @@ -0,0 +1,78 @@ +macro(compute_config_py_values + full_version_var_name + ) + string(TIMESTAMP PACKAGE_BUILD_DATE "%Y-%m-%dT%H:%M:%S+00:00" UTC) + if (PACKAGE_BUILD_DATE) + set(PACKAGE_BUILD_DATE "__build_date__ = '${PACKAGE_BUILD_DATE}'") + endif() + + if (PACKAGE_SETUP_PY_PACKAGE_VERSION) + set(PACKAGE_SETUP_PY_PACKAGE_VERSION_ASSIGNMENT "__setup_py_package_version__ = '${PACKAGE_SETUP_PY_PACKAGE_VERSION}'") + set(FINAL_PACKAGE_VERSION ${PACKAGE_SETUP_PY_PACKAGE_VERSION}) + else() + set(FINAL_PACKAGE_VERSION ${${full_version_var_name}}) + endif() + + if (PACKAGE_SETUP_PY_PACKAGE_TIMESTAMP) + set(PACKAGE_SETUP_PY_PACKAGE_TIMESTAMP_ASSIGNMENT "__setup_py_package_timestamp__ = '${PACKAGE_SETUP_PY_PACKAGE_TIMESTAMP}'") + else() + set(PACKAGE_SETUP_PY_PACKAGE_TIMESTAMP_ASSIGNMENT "") + 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 PACKAGE_SOURCE_IS_INSIDE_REPO + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + + if(PACKAGE_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 PACKAGE_BUILD_COMMIT_DATE + OUTPUT_STRIP_TRAILING_WHITESPACE) + if(PACKAGE_BUILD_COMMIT_DATE) + set(PACKAGE_BUILD_COMMIT_DATE "__build_commit_date__ = '${PACKAGE_BUILD_COMMIT_DATE}'") + endif() + unset(ENV{TZ}) + + execute_process( + COMMAND ${GIT_EXECUTABLE} rev-parse HEAD + OUTPUT_VARIABLE PACKAGE_BUILD_COMMIT_HASH + OUTPUT_STRIP_TRAILING_WHITESPACE) + if(PACKAGE_BUILD_COMMIT_HASH) + set(PACKAGE_BUILD_COMMIT_HASH "__build_commit_hash__ = '${PACKAGE_BUILD_COMMIT_HASH}'") + endif() + + execute_process( + COMMAND ${GIT_EXECUTABLE} describe HEAD + OUTPUT_VARIABLE PACKAGE_BUILD_COMMIT_HASH_DESCRIBED + OUTPUT_STRIP_TRAILING_WHITESPACE) + if(PACKAGE_BUILD_COMMIT_HASH_DESCRIBED) + set(PACKAGE_BUILD_COMMIT_HASH_DESCRIBED "__build_commit_hash_described__ = '${PACKAGE_BUILD_COMMIT_HASH_DESCRIBED}'") + endif() + + endif() + endif() + +endmacro() + +# Creates a new target called "${library_name}_generator" which +# depends on the mjb_rejected_classes.log file generated by shiboken. +# This target is added as a dependency to ${library_name} target. +# This file's timestamp informs cmake when the last generation was +# done, without force-updating the timestamps of the generated class +# cpp files. +# In practical terms this means that changing some injection code in +# an xml file that modifies only one specific class cpp file, will +# not force rebuilding all the cpp files, and thus allow for better +# incremental builds. +macro(create_generator_target library_name) + add_custom_target(${library_name}_generator DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/mjb_rejected_classes.log") + add_dependencies(${library_name} ${library_name}_generator) +endmacro() diff --git a/sources/pyside2-tools b/sources/pyside2-tools -Subproject f1b775537e7fbd718516749583b2abf1cb6adbc +Subproject e3e3caeba4aebd68dc301b23e89dc4f78d708d1 diff --git a/sources/pyside2/CMakeLists.txt b/sources/pyside2/CMakeLists.txt index 358c119ae..25598bb5e 100644 --- a/sources/pyside2/CMakeLists.txt +++ b/sources/pyside2/CMakeLists.txt @@ -8,8 +8,10 @@ cmake_policy(SET CMP0046 NEW) project(pysidebindings) -set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Macros/ +set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/../cmake_helpers/ + ${CMAKE_SOURCE_DIR}/cmake/Macros/ ${CMAKE_MODULE_PATH}) +include(helpers) option(USE_PYTHON_VERSION "Use specific python version to build pyside2." "") @@ -244,64 +246,7 @@ else() CACHE STRING "PySide2 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() - -if (PYSIDE_SETUP_PY_PACKAGE_VERSION) - set(PYSIDE_SETUP_PY_PACKAGE_VERSION_ASSIGNMENT "__setup_py_package_version__ = '${PYSIDE_SETUP_PY_PACKAGE_VERSION}'") - set(FINAL_PACKAGE_VERSION ${PYSIDE_SETUP_PY_PACKAGE_VERSION}) -else() - set(FINAL_PACKAGE_VERSION ${BINDING_API_VERSION_FULL}) -endif() - -if (PYSIDE_SETUP_PY_PACKAGE_TIMESTAMP) - set(PYSIDE_SETUP_PY_PACKAGE_TIMESTAMP_ASSIGNMENT "__setup_py_package_timestamp__ = '${PYSIDE_SETUP_PY_PACKAGE_TIMESTAMP}'") -else() - set(PYSIDE_SETUP_PY_PACKAGE_TIMESTAMP_ASSIGNMENT "__setup_py_package_timestamp__ = ''") -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() +compute_config_py_values(BINDING_API_VERSION) include(PySideModules) diff --git a/sources/pyside2/PySide2/CMakeLists.txt b/sources/pyside2/PySide2/CMakeLists.txt index 0263f7441..4709dd073 100644 --- a/sources/pyside2/PySide2/CMakeLists.txt +++ b/sources/pyside2/PySide2/CMakeLists.txt @@ -40,12 +40,18 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/__init__.py" "${CMAKE_CURRENT_BINARY_DIR}/support/__init__.py" COPYONLY) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/__init__.py" "${CMAKE_CURRENT_BINARY_DIR}/support/signature/__init__.py" COPYONLY) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/layout.py" + "${CMAKE_CURRENT_BINARY_DIR}/support/signature/layout.py" COPYONLY) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/loader.py" "${CMAKE_CURRENT_BINARY_DIR}/support/signature/loader.py" COPYONLY) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/mapping.py" "${CMAKE_CURRENT_BINARY_DIR}/support/signature/mapping.py" COPYONLY) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/parser.py" "${CMAKE_CURRENT_BINARY_DIR}/support/signature/parser.py" COPYONLY) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/lib/__init__.py" + "${CMAKE_CURRENT_BINARY_DIR}/support/signature/lib/__init__.py" COPYONLY) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/lib/enum_sig.py" + "${CMAKE_CURRENT_BINARY_DIR}/support/signature/lib/enum_sig.py" COPYONLY) if (PYTHON_VERSION_MAJOR EQUAL 3) else() configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/backport_inspect.py" diff --git a/sources/pyside2/PySide2/Qt3DCore/typesystem_3dcore.xml b/sources/pyside2/PySide2/Qt3DCore/typesystem_3dcore.xml index fb7a83ba7..013a49165 100644 --- a/sources/pyside2/PySide2/Qt3DCore/typesystem_3dcore.xml +++ b/sources/pyside2/PySide2/Qt3DCore/typesystem_3dcore.xml @@ -45,19 +45,41 @@ <namespace-type name="Qt3DCore"> <enum-type name="ChangeFlag" flags="ChangeFlags"/> <object-type name="QAbstractAspect"/> + <object-type name="QAbstractEngine"/> <object-type name="QAbstractSkeleton" since="5.10"/> <object-type name="QArmature" since="5.10"/> - <object-type name="QAspectEngine"/> + <object-type name="QAspectEngine"> + <modify-function signature="registerAspect(Qt3DCore::QAbstractAspect*)"> + <modify-argument index="this"> + <parent index="1" action="add"/> + </modify-argument> + </modify-function> + </object-type> <object-type name="QAspectJob"/> <object-type name="QBackendNode"> <enum-type name="Mode"/> </object-type> + <!-- TODO: Solve issues related to windows and a unresolved + external symbol + <object-type name="QBackendNodeMapper"/>--> <object-type name="QComponent"/> <object-type name="QComponentAddedChange"/> <object-type name="QComponentRemovedChange"/> <object-type name="QDynamicPropertyUpdatedChange"/> - <object-type name="QEntity"/> - <object-type name="QJoint" since="5.10"/> + <object-type name="QEntity"> + <modify-function signature="addComponent(Qt3DCore::QComponent*)"> + <modify-argument index="this"> + <parent index="1" action="add"/> + </modify-argument> + </modify-function> + </object-type> + <object-type name="QJoint" since="5.10"> + <modify-function signature="addChildJoint(Qt3DCore::QJoint*)"> + <modify-argument index="this"> + <parent index="1" action="add"/> + </modify-argument> + </modify-function> + </object-type> <object-type name="QNode"> <enum-type name="PropertyTrackingMode"/> </object-type> @@ -88,5 +110,10 @@ <!-- Disambiguate from QtGui/qtransform.h --> <include file-name="Qt3DCore/qtransform.h" location="global"/> </object-type> + <namespace-type name="Quick"> + <object-type name="QQmlAspectEngine"> + <enum-type name="Status"/> + </object-type> + </namespace-type> </namespace-type> </typesystem> diff --git a/sources/pyside2/PySide2/Qt3DInput/typesystem_3dinput.xml b/sources/pyside2/PySide2/Qt3DInput/typesystem_3dinput.xml index dd72c5c01..ebac94f03 100644 --- a/sources/pyside2/PySide2/Qt3DInput/typesystem_3dinput.xml +++ b/sources/pyside2/PySide2/Qt3DInput/typesystem_3dinput.xml @@ -45,7 +45,9 @@ <namespace-type name="Qt3DInput"> <object-type name="QAbstractActionInput"/> <object-type name="QAbstractAxisInput"/> - <object-type name="QAbstractPhysicalDevice"/> + <object-type name="QAbstractPhysicalDevice"> + <enum-type name="DeviceStatus"/> + </object-type> <object-type name="QAction"/> <object-type name="QActionInput"/> <object-type name="QAnalogAxisInput"/> @@ -57,29 +59,34 @@ <object-type name="QButtonAxisInput"/> <object-type name="QInputAspect"/> <object-type name="QInputChord"/> + <!-- On windows this raises the following error: + type 'Qt3DInput::QInputDeviceIntegration' is specified in typesystem, but not defined. + This could potentially lead to compilation errors. + <object-type name="QInputDeviceIntegration"/> + --> <object-type name="QInputSequence"/> <object-type name="QInputSettings"/> + <object-type name="QKeyboardDevice"/> <object-type name="QKeyboardHandler"/> - <object-type name="QKeyEvent"> - <modify-function signature="QKeyEvent(const Qt3DInput::QKeyEvent&)" remove="all"/> - </object-type> + <object-type name="QKeyEvent"/> <object-type name="QLogicalDevice"/> - <object-type name="QKeyboardDevice"/> <object-type name="QMouseDevice"> <enum-type name="Axis"/> </object-type> - <!-- Fixme: shiboken2 mistakenly thinks that Qt3DInput::QMouseEvent(::QMouseEvent) - is a copy constructor of Qt3DInput::QMouseEvent. Work around by suppressing them --> <object-type name="QMouseEvent"> <enum-type name="Buttons"/> <enum-type name="Modifiers"/> - <modify-function signature="QMouseEvent(const Qt3DInput::QMouseEvent&)" remove="all"/> </object-type> <object-type name="QWheelEvent"> <enum-type name="Buttons"/> <enum-type name="Modifiers"/> - <modify-function signature="QWheelEvent(const Qt3DInput::QWheelEvent&)" remove="all"/> </object-type> <object-type name="QMouseHandler"/> + <!-- On windows this raise the following error: + qt3dinput_module_wrapper.cpp.obj : error LNK2019: + unresolved external symbol "void __cdecl init_Qt3DInput_QPhysicalDeviceCreatedChangeBase(struct _object *)" + (?init_Qt3DInput_QPhysicalDeviceCreatedChangeBase@@YAXPAU_object@@@Z) referenced in function _PyInit_Qt3DInput + <object-type name="QPhysicalDeviceCreatedChangeBase"/> + --> </namespace-type> </typesystem> diff --git a/sources/pyside2/PySide2/QtCore/CMakeLists.txt b/sources/pyside2/PySide2/QtCore/CMakeLists.txt index 1d0b7d413..e583bd0f4 100644 --- a/sources/pyside2/PySide2/QtCore/CMakeLists.txt +++ b/sources/pyside2/PySide2/QtCore/CMakeLists.txt @@ -16,6 +16,7 @@ ${QtCore_GEN_DIR}/qabstracteventdispatcher_timerinfo_wrapper.cpp ${QtCore_GEN_DIR}/qabstracteventdispatcher_wrapper.cpp ${QtCore_GEN_DIR}/qabstractitemmodel_wrapper.cpp ${QtCore_GEN_DIR}/qabstractlistmodel_wrapper.cpp +${QtCore_GEN_DIR}/qabstractnativeeventfilter_wrapper.cpp ${QtCore_GEN_DIR}/qabstractproxymodel_wrapper.cpp ${QtCore_GEN_DIR}/qabstractstate_wrapper.cpp ${QtCore_GEN_DIR}/qabstracttablemodel_wrapper.cpp @@ -28,6 +29,15 @@ ${QtCore_GEN_DIR}/qbuffer_wrapper.cpp ${QtCore_GEN_DIR}/qbytearray_wrapper.cpp ${QtCore_GEN_DIR}/qbytearraymatcher_wrapper.cpp ${QtCore_GEN_DIR}/qchildevent_wrapper.cpp +${QtCore_GEN_DIR}/qcborarray_wrapper.cpp +${QtCore_GEN_DIR}/qcborerror_wrapper.cpp +${QtCore_GEN_DIR}/qcbormap_wrapper.cpp +${QtCore_GEN_DIR}/qcborparsererror_wrapper.cpp +${QtCore_GEN_DIR}/qcborstreamreader_wrapper.cpp +${QtCore_GEN_DIR}/qcborstringresultstring_wrapper.cpp +${QtCore_GEN_DIR}/qcborstringresultbytearray_wrapper.cpp +${QtCore_GEN_DIR}/qcborstreamwriter_wrapper.cpp +${QtCore_GEN_DIR}/qcborvalue_wrapper.cpp ${QtCore_GEN_DIR}/qcollator_wrapper.cpp ${QtCore_GEN_DIR}/qcollatorsortkey_wrapper.cpp ${QtCore_GEN_DIR}/qcommandlineoption_wrapper.cpp diff --git a/sources/pyside2/PySide2/QtCore/typesystem_core_common.xml b/sources/pyside2/PySide2/QtCore/typesystem_core_common.xml index 631403c3f..005fd0684 100644 --- a/sources/pyside2/PySide2/QtCore/typesystem_core_common.xml +++ b/sources/pyside2/PySide2/QtCore/typesystem_core_common.xml @@ -180,6 +180,8 @@ <enum-type name="QtMsgType"/> + <enum-type name="QCborSimpleType" since="5.12"/> + <enum-type name="QCborKnownTags" since="5.12"/> <primitive-type name="qint8"/> <primitive-type name="qint16"/> @@ -428,15 +430,15 @@ </add-conversion> <add-conversion type="PyDict" check="PyDict_CheckExact(%in)"> QVariant ret = QVariant_convertToVariantMap(%in); - %out = ret.isValid() ? ret : QVariant::fromValue<PySide::PyObjectWrapper>(%in); + %out = ret.isValid() ? ret : QVariant::fromValue(PySide::PyObjectWrapper(%in)); </add-conversion> <add-conversion type="PyList" check="PyList_Check(%in)"> QVariant ret = QVariant_convertToVariantList(%in); - %out = ret.isValid() ? ret : QVariant::fromValue<PySide::PyObjectWrapper>(%in); + %out = ret.isValid() ? ret : QVariant::fromValue(PySide::PyObjectWrapper(%in)); </add-conversion> <add-conversion type="PyObject"> // Is a shiboken type not known by Qt - %out = QVariant::fromValue<PySide::PyObjectWrapper>(%in); + %out = QVariant::fromValue(PySide::PyObjectWrapper(%in)); </add-conversion> </target-to-native> </conversion-rule> @@ -666,6 +668,39 @@ </conversion-rule> </primitive-type> + <value-type name="QCborError" since="5.12"> + <enum-type name="Code"/> + <include file-name="qcborcommon.h" location="global"/> + </value-type> + + <value-type name="QCborParserError" since="5.12"> + <include file-name="qcborvalue.h" location="global"/> + </value-type> + + <value-type name="QCborValue" since="5.12"> + <enum-type name="EncodingOption" flags="EncodingOptions"/> + <enum-type name="DiagnosticNotationOption" flags="DiagnosticNotationOptions"/> + <enum-type name="Type"/> + </value-type> + <value-type name="QCborArray" since="5.12"/> + <value-type name="QCborMap" since="5.12"/> + + <object-type name="QCborStreamReader" since="5.12"> + <enum-type name="StringResultCode"/> + <enum-type name="Type"/> + <include file-name="qcborstream.h" location="global"/> + <value-type name="StringResult" generate="no"/> + <!-- 64bit (qsizetype = long long) --> + <modify-function signature="readStringChunk(char*,long long)" remove="all"/> + <!-- 32bit (qsizetype = int) --> + <modify-function signature="readStringChunk(char*,int)" remove="all"/> + </object-type> + <typedef-type name="QCborStringResultString" source="QCborStreamReader::StringResult<QString>" since="5.12"/> + <typedef-type name="QCborStringResultByteArray" source="QCborStreamReader::StringResult<QByteArray>" since="5.12"/> + <object-type name="QCborStreamWriter" since="5.12"> + <include file-name="qcborstream.h" location="global"/> + </object-type> + <primitive-type name="QJsonObject"> <conversion-rule> <native-to-target> @@ -907,9 +942,6 @@ <rejection class="" enum-name="QtValidLicenseForGuiModule"/> <rejection class="" enum-name="QtValidLicenseForScriptModule"/> <rejection class="" enum-name="QtValidLicenseForHelpModule"/> - <rejection class="QAbstractEventDispatcher" function-name="filterEvent"/> - <rejection class="QAbstractEventDispatcher" function-name="filterNativeEvent"/> - <rejection class="QAbstractEventDispatcher" function-name="setEventFilter"/> <!-- Internal --> <rejection class="QAbstractFileEngine"/> <!-- <rejection class="QAbstractFileEngine" function-name="endEntryList"/> @@ -964,7 +996,7 @@ <enum-type name="InputMethodHint" flags="InputMethodHints" since="4.6"/> <enum-type name="InputMethodQuery" flags="InputMethodQueries" /> <enum-type name="EnterKeyType" since="5.6" /> - <enum-type name="ItemDataRole" force-integer="yes" /> + <enum-type name="ItemDataRole"/> <enum-type name="ItemFlag" flags="ItemFlags"/> <enum-type name="ItemSelectionMode"/> <enum-type name="ItemSelectionOperation" since="5.5" /> @@ -1012,11 +1044,6 @@ <enum-type name="WindowType" flags="WindowFlags"/> <enum-type name="CursorMoveStyle" since="4.8" revision="4800"/> - <!--### These functions are part of QtGui, not QtCore --> - <modify-function signature="codecForHtml(const QByteArray&)" remove="all"/> - <modify-function signature="mightBeRichText(const QString&)" remove="all"/> - <modify-function signature="convertFromPlainText(const QString&,Qt::WhiteSpaceMode)" remove="all"/> - <!--### --> </namespace-type> <add-function signature="qAbs(double)" return-type="double"> @@ -1075,7 +1102,7 @@ qWarning() << "Module atexit not found for registering __moduleShutdown"; PyErr_Clear(); }else{ - regFunc = PyObject_GetAttrString(atexit, "register"); + regFunc.reset(PyObject_GetAttrString(atexit, "register")); if (regFunc.isNull()) { qWarning() << "Function atexit.register not found for registering __moduleShutdown"; PyErr_Clear(); @@ -2072,7 +2099,7 @@ </object-type> <object-type name="QAbstractItemModel"> - <enum-type name="CheckIndexOption" flags="CheckIndexOptions" class="yes" since="5.11"/> + <enum-type name="CheckIndexOption" flags="CheckIndexOptions" since="5.11"/> <enum-type name="LayoutChangeHint" /> <!-- This function was replaced by a added function --> <modify-function signature="createIndex(int,int,void*)const" remove="all"/> @@ -2132,7 +2159,6 @@ <value-type name="QItemSelectionRange" hash-function="qHash"> </value-type> - <primitive-type name="QModelIndexList"/> <object-type name="QAbstractProxyModel" polymorphic-id-expression="qobject_cast<QAbstractProxyModel*>(%1)"> <extra-includes> <include file-name="QItemSelection" location="global"/> @@ -2141,9 +2167,6 @@ </extra-includes> </object-type> <object-type name="QSortFilterProxyModel"> - <!-- ### This reimplementation of "QObject::parent()" is used in C++ only - when "using QObject::parent;" is not available. It's useless in Python. --> - <modify-function signature="parent()const" remove="all"/> <extra-includes> <include file-name="QItemSelection" location="global"/> <include file-name="QStringList" location="global"/> @@ -2734,12 +2757,12 @@ } </inject-code> </modify-function> - <add-function signature="QByteArray(PyByteArray)" allow-thread="yes">> + <add-function signature="QByteArray(PyByteArray)"> <inject-code class="target" position="beginning"> %0 = new QByteArray(PyByteArray_AsString(%PYARG_1), PyByteArray_Size(%PYARG_1)); </inject-code> </add-function> - <add-function signature="QByteArray(PyBytes)" allow-thread="yes"> + <add-function signature="QByteArray(PyBytes)"> <inject-code class="target" position="beginning"> %0 = new QByteArray(PyBytes_AS_STRING(%PYARG_1), PyBytes_GET_SIZE(%PYARG_1)); </inject-code> @@ -2956,6 +2979,26 @@ <object-type name="QXmlStreamEntityResolver"/> <!-- Qt5: had to move QAbstractEventDispatcher into os-specific files because of Windows --> + <object-type name="QAbstractNativeEventFilter"> + <!-- see QWidget::nativeEvent(), QWindow::nativeEvent() --> + <modify-function signature="nativeEventFilter(const QByteArray&,void*,long*)"> + <modify-argument index="3"> + <remove-argument/> + <conversion-rule class="native"> + <insert-template name="return_native_eventfilter_conversion_variables"/> + </conversion-rule> + </modify-argument> + <modify-argument index="return"> + <replace-type modified-type="PyObject"/> + <conversion-rule class="native"> + <insert-template name="return_native_eventfilter_conversion"/> + </conversion-rule> + </modify-argument> + <inject-code position="end"> + <insert-template name="return_native_eventfilter"/> + </inject-code> + </modify-function> + </object-type> <object-type name="QEventLoop"> <enum-type name="ProcessEventsFlag" flags="ProcessEventsFlags"/> @@ -3445,31 +3488,6 @@ <define-ownership owner="c++"/> </modify-argument> </modify-function> - <modify-function signature="winEventFilter(MSG*,long*)"> - <modify-argument index="2"> - <remove-argument /> - <conversion-rule class="native"> - long *%out = new long; - %out = 0; - </conversion-rule> - </modify-argument> - <modify-argument index="return"> - <replace-type modified-type="PyObject"/> - <conversion-rule class="native"> - %RETURN_TYPE %out = false; - if (PySequence_Check(%PYARG_0) && (PySequence_Size(%PYARG_0) == 2)) { - Shiboken::AutoDecRef pyResult(PySequence_GetItem(%PYARG_0, 0)); - %out = %CONVERTTOCPP[bool](pyResult); - } - </conversion-rule> - </modify-argument> - <inject-code position="end"> - %PYARG_0 = PyTuple_New(2); - PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](%0)); - PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[long](*result_out)); - delete result_out; - </inject-code> - </modify-function> </object-type> <object-type name="QSettings"> <enum-type name="Format"/> @@ -3494,10 +3512,9 @@ </modify-function> </object-type> <object-type name="QEvent" polymorphic-id-expression="%1->type() == QEvent::None"> - <enum-type name="Type" extensible="yes" /> + <enum-type name="Type"/> </object-type> <object-type name="QChildEvent" polymorphic-id-expression="%1->type() == QEvent::ChildAdded || %1->type() == QEvent::ChildPolished || %1->type() == QEvent::ChildRemoved"> - <modify-field name="c" read="false" write="false"/> <modify-function signature="child()const"> <modify-argument index="return"> <define-ownership class="target" owner="default"/> @@ -3949,9 +3966,6 @@ </modify-function> <!-- This isn't part of Qt public API --> <modify-function signature="enclosingMetaObject()const" remove="all" /> - <!-- Qt5.5: "template<typename T> static QMetaEnum fromType()" is not understood by the compiler. - We therefore ignore this 5.5 addition for now: --> - <modify-function signature="fromType()" since="5.5" remove="all" /> </value-type> <!-- From Qt4.6 --> @@ -4392,28 +4406,30 @@ s1.addTransition(button.clicked, s1h)</code> <suppress-warning text="unhandled enum value: (sizeof(void*)<<3) in QSysInfo::Sizes"/> <suppress-warning text="unmatched enum ~0u"/> <suppress-warning text="unmatched enum (sizeof(void*)<<3)"/> - <suppress-warning text="signature 'setCustomType(float)' for function modification in 'QEasingCurve' not found. Possible candidates: setCustomType(double) in QEasingCurve"/> + <suppress-warning text="^signature 'setCustomType(float)' for function modification in 'QEasingCurve' not found.*$"/> <suppress-warning text="enum 'enum_4' does not have a type entry or is not an enum" /> <suppress-warning text="enum 'enum_5' does not have a type entry or is not an enum" /> <suppress-warning text="enum 'FP_NORMAL' does not have a type entry or is not an enum" /> <suppress-warning text="Shadowing: *" /> + <!-- QCborStreamReader: Suppress warnings about 32/64bit signatures not found depending on qsizetype --> + <suppress-warning text="^signature 'readStringChunk\(char.*in 'QCborStreamReader' not found.*$"/> <!-- this enum is defined on Qt global header but only used in QtGui module --> <suppress-warning text="enum 'PM_MessageBoxHeight' does not have a type entry or is not an enum" /> <!-- this function only exists on Windows --> - <suppress-warning text="signature 'winEventFilter(MSG*,long*)' for function modification in 'QCoreApplication' not found. Possible candidates:*"/> + <suppress-warning text="^signature 'winEventFilter(MSG*,long*)' for function modification in 'QCoreApplication' not found.*"/> <!-- this is necessary to avoid warning on other modules --> - <suppress-warning text="signature 'operator*(QByteArray,const char*)' for function modification in 'QByteArray' not found. Possible candidates:*"/> - <suppress-warning text="signature 'operator+(QByteArray,QString)' for function modification in 'QByteArray' not found. Possible candidates:*"/> + <suppress-warning text="^signature 'operator*(QByteArray,const char*)' for function modification in 'QByteArray' not found.*"/> + <suppress-warning text="^signature 'operator+(QByteArray,QString)' for function modification in 'QByteArray' not found.*"/> <!-- This enum is intenaly used --> <suppress-warning text="enum 'PM_CbaIconHeight' does not have a type entry or is not an enum" /> <!-- TODO: this need be removed --> - <suppress-warning text="skipping function '*', unmatched return type '*'"/> - <suppress-warning text="skipping function '*', unmatched type '*"/> + <suppress-warning text="^skipping function '.*', unmatched return type '.*$"/> + <suppress-warning text="^skipping function '.*', unmatched type '.*$"/> <suppress-warning text="enum 'q_static_assert_result39' does not have a type entry or is not an enum"/> <suppress-warning text="horribly broken type ''"/> @@ -4432,7 +4448,7 @@ s1.addTransition(button.clicked, s1h)</code> <suppress-warning text="enum 'Role' does not have a type entry or is not an enum" /> <!-- Anonymous enum in qtbase/src/corelib/global/qtypeinfo.h --> - <suppress-warning text="enum 'Q_RELOCATABLE_TYPE' does not have a type entry or is not an enum" /> + <suppress-warning text="Anonymous enum (Q_COMPLEX_TYPE, ... , Q_RELOCATABLE_TYPE) does not have a type entry"/> <!-- Another anonymous enum / value pair in in qtbase/src/corelib/kernel/qcoreapplication.h --> <suppress-warning text="no matching enum 'QT_VERSION'" /> diff --git a/sources/pyside2/PySide2/QtGui/CMakeLists.txt b/sources/pyside2/PySide2/QtGui/CMakeLists.txt index 1fe743c01..b330a63bf 100644 --- a/sources/pyside2/PySide2/QtGui/CMakeLists.txt +++ b/sources/pyside2/PySide2/QtGui/CMakeLists.txt @@ -2,11 +2,9 @@ project(QtGui) qt5_wrap_cpp(QPYTEXTOBJECT_MOC "${pyside2_SOURCE_DIR}/qpytextobject.h") -set(QtGui_OPTIONAL_SRC) set(QtGui_DROPPED_ENTRIES) -check_qt_class(QtGui QOpenGLTimeMonitor QtGui_OPTIONAL_SRC QtGui_DROPPED_ENTRIES) -check_qt_class(QtGui QOpenGLTimerQuery QtGui_OPTIONAL_SRC QtGui_DROPPED_ENTRIES) +get_property(QtGui_enabled_features TARGET Qt5::Gui PROPERTY INTERFACE_QT_ENABLED_FEATURES) set(QtGui_SRC ${QtGui_GEN_DIR}/qabstractopenglfunctions_wrapper.cpp @@ -207,11 +205,23 @@ ${QtGui_GEN_DIR}/qwhatsthisclickedevent_wrapper.cpp ${QtGui_GEN_DIR}/qwheelevent_wrapper.cpp ${QtGui_GEN_DIR}/qwindow_wrapper.cpp ${QtGui_GEN_DIR}/qwindowstatechangeevent_wrapper.cpp -${QtGui_OPTIONAL_SRC} # module is always needed ${QtGui_GEN_DIR}/qtgui_module_wrapper.cpp ) +# cf qtbase/src/gui/opengl/opengl.pri +list(FIND QtGui_enabled_features "opengles2" _opengles2Index) +# ### fixme: For cmake >= 3.3: if(opengles2 IN_LIST QtGui_enabled_features) +if(_opengles2Index GREATER -1) + list(APPEND QtGui_DROPPED_ENTRIES QOpenGLTimeMonitor QOpenGLTimerQuery) + message(STATUS "Qt5Gui: Dropping Desktop OpenGL classes (GLES2)") +else() + list(APPEND QtGui_SRC + ${QtGui_GEN_DIR}/qopengltimemonitor_wrapper.cpp + ${QtGui_GEN_DIR}/qopengltimerquery_wrapper.cpp) + message(STATUS "Qt5Gui: Adding Desktop OpenGL classes") +endif() + configure_file("${QtGui_SOURCE_DIR}/typesystem_gui.xml.in" "${QtGui_BINARY_DIR}/typesystem_gui.xml" @ONLY) diff --git a/sources/pyside2/PySide2/QtGui/typesystem_gui_common.xml b/sources/pyside2/PySide2/QtGui/typesystem_gui_common.xml index 70fd1692a..e2e3b2335 100644 --- a/sources/pyside2/PySide2/QtGui/typesystem_gui_common.xml +++ b/sources/pyside2/PySide2/QtGui/typesystem_gui_common.xml @@ -97,21 +97,21 @@ <rejection class="*" function-name="d_func"/> <rejection class="*" field-name="d_ptr"/> <rejection class="*" field-name="d"/> - <rejection class="^QOpenGL.*$" argument-type="^GLboolean( const)?\*$"/> + <rejection class="^QOpenGL.*$" argument-type="^(const )?GLboolean ?\*$"/> <rejection class="^QOpenGL.*$" argument-type="^GLchar\*$"/> - <rejection class="^QOpenGL.*$" argument-type="GLchar *const const*"/> + <rejection class="^QOpenGL.*$" argument-type="^(const )?GLchar ?\*(const)?\*$"/> <rejection class="^QOpenGL.*$" argument-type="^char\*$"/> - <rejection class="^QOpenGL.*$" argument-type="^char( const)?\*\*$"/> + <rejection class="^QOpenGL.*$" argument-type="^(const )?char ?\*\*$"/> <rejection class="^QOpenGL.*$" argument-type="GLintptr"/> <rejection class="^QOpenGL.*$" argument-type="GLsizeiptr"/> <rejection class="^QOpenGL.*$" argument-type="GLsync"/> <rejection class="^QOpenGL.*$" argument-type="^GLubyte( const)?\*$"/> - <rejection class="^QOpenGL.*$" argument-type="^QMatrix.x.( const)?\*$"/> + <rejection class="^QOpenGL.*$" argument-type="^(const )?QMatrix.x. ?\*$"/> <rejection class="^QOpenGL.*$" argument-type="qopengl_GLintptr"/> <rejection class="^QOpenGL.*$" argument-type="qopengl_GLsizeiptr"/> <rejection class="^QOpenGL.*$" argument-type="QOpenGLTextureHelper*"/> - <rejection class="^QOpenGL.*$" argument-type="^QVector.D( const)?\*$"/> - <rejection class="^QOpenGL.*$" argument-type="^void( const)?\*\*$"/> + <rejection class="^QOpenGL.*$" argument-type="^(const )?QVector.D ?\*$"/> + <rejection class="^QOpenGL.*$" argument-type="^(const )?void ?\*\*$"/> <!-- Event classes have a lot of non-documented protected fields, those fields @@ -395,7 +395,6 @@ <value-type name="QTextFrameFormat" > <enum-type name="BorderStyle"/> <enum-type name="Position"/> - <modify-function signature="isValid()const" access="non-final"/> </value-type> <value-type name="QTextLength"> <enum-type name="Type"/> @@ -403,9 +402,6 @@ <value-type name="QPainterPath"> <enum-type name="ElementType"/> <value-type name="Element"> - <modify-field name="x" write="false"/> - <modify-field name="y" write="false"/> - <modify-field name="type" write="false"/> <include file-name="QPainterPath" location="global"/> </value-type> </value-type> @@ -554,14 +550,12 @@ <enum-type name="FontPropertiesInheritanceBehavior"/> <enum-type name="UnderlineStyle"/> <enum-type name="VerticalAlignment"/> - <modify-function signature="isValid()const" access="non-final"/> </value-type> <value-type name="QTextFormat" > <enum-type name="FormatType"/> <enum-type name="ObjectTypes"/> <enum-type name="PageBreakFlag" flags="PageBreakFlags"/> <enum-type name="Property" /> - <modify-function signature="isValid()const" access="non-final"/> </value-type> <value-type name="QTextListFormat"> <enum-type name="Style"/> @@ -668,7 +662,7 @@ %0 = new %TYPE(QPixmap::fromImage(%1)); </inject-code> </add-function> - <modify-function signature="QPixmap(const char*[])"> + <modify-function signature="QPixmap(const char*const[])"> <modify-argument index="1"> <replace-type modified-type="PySequence" /> </modify-argument> @@ -897,7 +891,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*const[])"> <modify-argument index="1"> <replace-type modified-type="PySequence" /> </modify-argument> @@ -1507,10 +1501,11 @@ </inject-code> </modify-function> </value-type> - <value-type name="QGradient" force-abstract="yes" polymorphic-id-expression="%1->type() == QGradient::NoGradient"> + <value-type name="QGradient" polymorphic-id-expression="%1->type() == QGradient::NoGradient"> <enum-type name="CoordinateMode"/> <enum-type name="InterpolationMode"/> - <enum-type name="Spread" lower-bound="QGradient.PadSpread" upper-bound="QGradient.RepeatSpread"/> + <enum-type name="Preset" since="5.12"/> + <enum-type name="Spread"/> <enum-type name="Type"/> </value-type> <value-type name="QLinearGradient" polymorphic-id-expression="%1->type() == QGradient::LinearGradient" /> @@ -1741,7 +1736,7 @@ else qWarning("%TYPE::%FUNCTION_NAME: Second tuple element is not convertible to int."); } - %PYARG_0 = PySequence_Fast_GET_ITEM(seq.object(), 0); + %PYARG_0.reset(PySequence_Fast_GET_ITEM(seq.object(), 0)); Py_INCREF(%PYARG_0); // we need to incref, because "%PYARG_0 = ..." will decref the tuple and the tuple will be decrefed again at the end of this scope. } @@ -1835,9 +1830,7 @@ <suppress-warning text="enum 'QWheelEvent::DefaultDeltasPerStep' does not have a type entry or is not an enum" /> <object-type name="QWindowStateChangeEvent" copyable="false" polymorphic-id-expression="%1->type() == QEvent::WindowStateChange"/> - <object-type name="QInputEvent" copyable="false"> - <modify-function signature="modifiers()const" access="non-final"/> - </object-type> + <object-type name="QInputEvent" copyable="false"/> <object-type name="QKeyEvent" copyable= "false" polymorphic-id-expression="%1->type() == QEvent::KeyPress || %1->type() == QEvent::KeyRelease || %1->type() == QEvent::ShortcutOverride"> <add-function signature="operator!=(QKeySequence::StandardKey)"> <inject-code class="target"> @@ -2237,7 +2230,6 @@ </extra-includes> <!-- ### "setPaintDevice(QPaintDevice*)" is an internal method. --> <modify-function signature="setPaintDevice(QPaintDevice*)" remove="all"/> - <modify-field name="state" read="false" write="false"/> </object-type> <object-type name="QPainter"> <extra-includes> @@ -2388,7 +2380,12 @@ </modify-function> </object-type> + <value-type name="QGenericMatrix" generate="no"/> <value-type name="QMatrix2x2" since="4.6"> + <modify-function signature="QMatrix2x2(const float*)"> + <modify-argument index="1"><array/></modify-argument> + </modify-function> + <modify-function signature="copyDataTo(float*) const" remove="all"/> <add-function signature="__repr__" return-type="PyObject*"> <inject-code class="target" position="beginning"> <insert-template name="repr_code_matrix"> @@ -2420,63 +2417,13 @@ </insert-template> </inject-code> </add-function> - <add-function signature="fill(PyObject*)"> - <inject-code class="target" position="beginning"> - <insert-template name="matrix_fill_function"> - <replace from="%MATRIX_SIZE" to="4" /> - </insert-template> - </inject-code> - </add-function> - <add-function signature="transposed()" return-type="PyObject"> - <inject-code class="target" position="beginning"> - <insert-template name="matrix_transposed_function"> - <replace from="%TRANSPOSED_TYPE" to="QMatrix2x2" /> - </insert-template> - </inject-code> - </add-function> - <add-function signature="operator!=(const QMatrix2x2&)" return-type="bool" /> - - <template name="inplace_add"> - *%CPPSELF += %1; - return %CONVERTTOPYTHON[%RETURN_TYPE](*%CPPSELF); - </template> - <template name="inplace_sub"> - *%CPPSELF -= %1; - return %CONVERTTOPYTHON[%RETURN_TYPE](*%CPPSELF); - </template> - <template name="inplace_mult"> - *%CPPSELF *= %1; - return %CONVERTTOPYTHON[%RETURN_TYPE](*%CPPSELF); - </template> - <template name="inplace_div"> - *%CPPSELF /= %1; - return %CONVERTTOPYTHON[%RETURN_TYPE](*%CPPSELF); - </template> - - <add-function signature="operator*=(float)" return-type="QMatrix2x2" > - <inject-code class="target" position="beginning"> - <insert-template name="inplace_mult"/> - </inject-code> - </add-function> - <add-function signature="operator+=(const QMatrix2x2&)" return-type="QMatrix2x2" > - <inject-code class="target" position="beginning"> - <insert-template name="inplace_add"/> - </inject-code> - </add-function> - <add-function signature="operator-=(const QMatrix2x2&)" return-type="QMatrix2x2" > - <inject-code class="target" position="beginning"> - <insert-template name="inplace_sub"/> - </inject-code> - </add-function> - <add-function signature="operator/=(float)" return-type="QMatrix2x2" > - <inject-code class="target" position="beginning"> - <insert-template name="inplace_div"/> - </inject-code> - </add-function> - <add-function signature="operator==(const QMatrix2x2&)" return-type="bool" /> </value-type> <value-type name="QMatrix2x3" since="4.6"> + <modify-function signature="QMatrix2x3(const float*)"> + <modify-argument index="1"><array/></modify-argument> + </modify-function> + <modify-function signature="copyDataTo(float*) const" remove="all"/> <add-function signature="__repr__" return-type="PyObject*"> <inject-code class="target" position="beginning"> <insert-template name="repr_code_matrix"> @@ -2508,45 +2455,13 @@ </insert-template> </inject-code> </add-function> - <add-function signature="fill(PyObject*)"> - <inject-code class="target" position="beginning"> - <insert-template name="matrix_fill_function"> - <replace from="%MATRIX_SIZE" to="6" /> - </insert-template> - </inject-code> - </add-function> - <add-function signature="transposed()" return-type="PyObject"> - <inject-code class="target" position="beginning"> - <insert-template name="matrix_transposed_function"> - <replace from="%TRANSPOSED_TYPE" to="QMatrix3x2" /> - </insert-template> - </inject-code> - </add-function> - <add-function signature="operator!=(const QMatrix2x3&)" return-type="bool" /> - <add-function signature="operator*=(float)" return-type="QMatrix2x3" > - <inject-code class="target" position="beginning"> - <insert-template name="inplace_mult"/> - </inject-code> - </add-function> - <add-function signature="operator+=(const QMatrix2x3&)" return-type="QMatrix2x3" > - <inject-code class="target" position="beginning"> - <insert-template name="inplace_add"/> - </inject-code> - </add-function> - <add-function signature="operator-=(const QMatrix2x3&)" return-type="QMatrix2x3" > - <inject-code class="target" position="beginning"> - <insert-template name="inplace_sub"/> - </inject-code> - </add-function> - <add-function signature="operator/=(float)" return-type="QMatrix2x3" > - <inject-code class="target" position="beginning"> - <insert-template name="inplace_div"/> - </inject-code> - </add-function> - <add-function signature="operator==(const QMatrix2x3&)" return-type="bool" /> </value-type> <value-type name="QMatrix2x4" since="4.6"> + <modify-function signature="QMatrix2x4(const float*)"> + <modify-argument index="1"><array/></modify-argument> + </modify-function> + <modify-function signature="copyDataTo(float*) const" remove="all"/> <add-function signature="__repr__" return-type="PyObject*"> <inject-code class="target" position="beginning"> <insert-template name="repr_code_matrix"> @@ -2578,45 +2493,13 @@ </insert-template> </inject-code> </add-function> - <add-function signature="fill(PyObject*)"> - <inject-code class="target" position="beginning"> - <insert-template name="matrix_fill_function"> - <replace from="%MATRIX_SIZE" to="8" /> - </insert-template> - </inject-code> - </add-function> - <add-function signature="transposed()" return-type="PyObject"> - <inject-code class="target" position="beginning"> - <insert-template name="matrix_transposed_function"> - <replace from="%TRANSPOSED_TYPE" to="QMatrix4x2" /> - </insert-template> - </inject-code> - </add-function> - <add-function signature="operator!=(const QMatrix2x4&)" return-type="bool" /> - <add-function signature="operator*=(float)" return-type="QMatrix2x4" > - <inject-code class="target" position="beginning"> - <insert-template name="inplace_mult"/> - </inject-code> - </add-function> - <add-function signature="operator+=(const QMatrix2x4&)" return-type="QMatrix2x4" > - <inject-code class="target" position="beginning"> - <insert-template name="inplace_add"/> - </inject-code> - </add-function> - <add-function signature="operator-=(const QMatrix2x4&)" return-type="QMatrix2x4" > - <inject-code class="target" position="beginning"> - <insert-template name="inplace_sub"/> - </inject-code> - </add-function> - <add-function signature="operator/=(float)" return-type="QMatrix2x4" > - <inject-code class="target" position="beginning"> - <insert-template name="inplace_div"/> - </inject-code> - </add-function> - <add-function signature="operator==(const QMatrix2x4&)" return-type="bool" /> </value-type> <value-type name="QMatrix3x2" since="4.6"> + <modify-function signature="QMatrix3x2(const float*)"> + <modify-argument index="1"><array/></modify-argument> + </modify-function> + <modify-function signature="copyDataTo(float*) const" remove="all"/> <add-function signature="__repr__" return-type="PyObject*"> <inject-code class="target" position="beginning"> <insert-template name="repr_code_matrix"> @@ -2648,45 +2531,13 @@ </insert-template> </inject-code> </add-function> - <add-function signature="fill(PyObject*)"> - <inject-code class="target" position="beginning"> - <insert-template name="matrix_fill_function"> - <replace from="%MATRIX_SIZE" to="6" /> - </insert-template> - </inject-code> - </add-function> - <add-function signature="transposed()" return-type="PyObject"> - <inject-code class="target" position="beginning"> - <insert-template name="matrix_transposed_function"> - <replace from="%TRANSPOSED_TYPE" to="QMatrix2x3" /> - </insert-template> - </inject-code> - </add-function> - <add-function signature="operator!=(const QMatrix3x2&)" return-type="bool" /> - <add-function signature="operator*=(float)" return-type="QMatrix3x2" > - <inject-code class="target" position="beginning"> - <insert-template name="inplace_mult"/> - </inject-code> - </add-function> - <add-function signature="operator+=(const QMatrix3x2&)" return-type="QMatrix3x2" > - <inject-code class="target" position="beginning"> - <insert-template name="inplace_add"/> - </inject-code> - </add-function> - <add-function signature="operator-=(const QMatrix3x2&)" return-type="QMatrix3x2" > - <inject-code class="target" position="beginning"> - <insert-template name="inplace_sub"/> - </inject-code> - </add-function> - <add-function signature="operator/=(float)" return-type="QMatrix3x2" > - <inject-code class="target" position="beginning"> - <insert-template name="inplace_div"/> - </inject-code> - </add-function> - <add-function signature="operator==(const QMatrix3x2&)" return-type="bool" /> </value-type> <value-type name="QMatrix3x3" since="4.6"> + <modify-function signature="QMatrix3x3(const float*)"> + <modify-argument index="1"><array/></modify-argument> + </modify-function> + <modify-function signature="copyDataTo(float*) const" remove="all"/> <add-function signature="__repr__" return-type="PyObject*"> <inject-code class="target" position="beginning"> <insert-template name="repr_code_matrix"> @@ -2718,45 +2569,13 @@ </insert-template> </inject-code> </add-function> - <add-function signature="fill(PyObject*)"> - <inject-code class="target" position="beginning"> - <insert-template name="matrix_fill_function"> - <replace from="%MATRIX_SIZE" to="9" /> - </insert-template> - </inject-code> - </add-function> - <add-function signature="transposed()" return-type="PyObject"> - <inject-code class="target" position="beginning"> - <insert-template name="matrix_transposed_function"> - <replace from="%TRANSPOSED_TYPE" to="QMatrix3x3" /> - </insert-template> - </inject-code> - </add-function> - <add-function signature="operator!=(const QMatrix3x3&)" return-type="bool" /> - <add-function signature="operator*=(float)" return-type="QMatrix3x3" > - <inject-code class="target" position="beginning"> - <insert-template name="inplace_mult"/> - </inject-code> - </add-function> - <add-function signature="operator+=(const QMatrix3x3&)" return-type="QMatrix3x3" > - <inject-code class="target" position="beginning"> - <insert-template name="inplace_add"/> - </inject-code> - </add-function> - <add-function signature="operator-=(const QMatrix3x3&)" return-type="QMatrix3x3" > - <inject-code class="target" position="beginning"> - <insert-template name="inplace_sub"/> - </inject-code> - </add-function> - <add-function signature="operator/=(float)" return-type="QMatrix3x3" > - <inject-code class="target" position="beginning"> - <insert-template name="inplace_div"/> - </inject-code> - </add-function> - <add-function signature="operator==(const QMatrix3x3&)" return-type="bool" /> </value-type> <value-type name="QMatrix3x4" since="4.6"> + <modify-function signature="QMatrix3x4(const float*)"> + <modify-argument index="1"><array/></modify-argument> + </modify-function> + <modify-function signature="copyDataTo(float*) const" remove="all"/> <add-function signature="__repr__" return-type="PyObject*"> <inject-code class="target" position="beginning"> <insert-template name="repr_code_matrix"> @@ -2788,45 +2607,12 @@ </insert-template> </inject-code> </add-function> - <add-function signature="fill(PyObject*)"> - <inject-code class="target" position="beginning"> - <insert-template name="matrix_fill_function"> - <replace from="%MATRIX_SIZE" to="12" /> - </insert-template> - </inject-code> - </add-function> - <add-function signature="transposed()" return-type="PyObject"> - <inject-code class="target" position="beginning"> - <insert-template name="matrix_transposed_function"> - <replace from="%TRANSPOSED_TYPE" to="QMatrix4x3" /> - </insert-template> - </inject-code> - </add-function> - <add-function signature="operator!=(const QMatrix3x4&)" return-type="bool" /> - <add-function signature="operator*=(float)" return-type="QMatrix3x4" > - <inject-code class="target" position="beginning"> - <insert-template name="inplace_mult"/> - </inject-code> - </add-function> - <add-function signature="operator+=(const QMatrix3x4&)" return-type="QMatrix3x4" > - <inject-code class="target" position="beginning"> - <insert-template name="inplace_add"/> - </inject-code> - </add-function> - <add-function signature="operator-=(const QMatrix3x4&)" return-type="QMatrix3x4" > - <inject-code class="target" position="beginning"> - <insert-template name="inplace_sub"/> - </inject-code> - </add-function> - <add-function signature="operator/=(float)" return-type="QMatrix3x4" > - <inject-code class="target" position="beginning"> - <insert-template name="inplace_div"/> - </inject-code> - </add-function> - <add-function signature="operator==(const QMatrix3x4&)" return-type="bool" /> </value-type> <value-type name="QMatrix4x2" since="4.6"> + <modify-function signature="QMatrix4x2(const float*)"> + <modify-argument index="1"><array/></modify-argument> + </modify-function> <add-function signature="__repr__" return-type="PyObject*"> <inject-code class="target" position="beginning"> <insert-template name="repr_code_matrix"> @@ -2858,45 +2644,13 @@ </insert-template> </inject-code> </add-function> - <add-function signature="fill(PyObject*)"> - <inject-code class="target" position="beginning"> - <insert-template name="matrix_fill_function"> - <replace from="%MATRIX_SIZE" to="8" /> - </insert-template> - </inject-code> - </add-function> - <add-function signature="transposed()" return-type="PyObject"> - <inject-code class="target" position="beginning"> - <insert-template name="matrix_transposed_function"> - <replace from="%TRANSPOSED_TYPE" to="QMatrix2x4" /> - </insert-template> - </inject-code> - </add-function> - <add-function signature="operator!=(const QMatrix4x2&)" return-type="bool" /> - <add-function signature="operator*=(float)" return-type="QMatrix4x2" > - <inject-code class="target" position="beginning"> - <insert-template name="inplace_mult"/> - </inject-code> - </add-function> - <add-function signature="operator+=(const QMatrix4x2&)" return-type="QMatrix4x2" > - <inject-code class="target" position="beginning"> - <insert-template name="inplace_add"/> - </inject-code> - </add-function> - <add-function signature="operator-=(const QMatrix4x2&)" return-type="QMatrix4x2" > - <inject-code class="target" position="beginning"> - <insert-template name="inplace_sub"/> - </inject-code> - </add-function> - <add-function signature="operator/=(float)" return-type="QMatrix4x2" > - <inject-code class="target" position="beginning"> - <insert-template name="inplace_div"/> - </inject-code> - </add-function> - <add-function signature="operator==(const QMatrix4x2&)" return-type="bool" /> </value-type> <value-type name="QMatrix4x3" since="4.6"> + <modify-function signature="QMatrix4x3(const float*)"> + <modify-argument index="1"><array/></modify-argument> + </modify-function> + <modify-function signature="copyDataTo(float*) const" remove="all"/> <add-function signature="__repr__" return-type="PyObject*"> <inject-code class="target" position="beginning"> <insert-template name="repr_code_matrix"> @@ -2928,47 +2682,10 @@ </insert-template> </inject-code> </add-function> - <add-function signature="fill(PyObject*)"> - <inject-code class="target" position="beginning"> - <insert-template name="matrix_fill_function"> - <replace from="%MATRIX_SIZE" to="12" /> - </insert-template> - </inject-code> - </add-function> - <add-function signature="transposed()" return-type="PyObject"> - <inject-code class="target" position="beginning"> - <insert-template name="matrix_transposed_function"> - <replace from="%TRANSPOSED_TYPE" to="QMatrix3x4" /> - </insert-template> - </inject-code> - </add-function> - <add-function signature="operator!=(const QMatrix4x3&)" return-type="bool" /> - <add-function signature="operator*=(float)" return-type="QMatrix4x3" > - <inject-code class="target" position="beginning"> - <insert-template name="inplace_mult"/> - </inject-code> - </add-function> - <add-function signature="operator+=(const QMatrix4x3&)" return-type="QMatrix4x3" > - <inject-code class="target" position="beginning"> - <insert-template name="inplace_add"/> - </inject-code> - </add-function> - <add-function signature="operator-=(const QMatrix4x3&)" return-type="QMatrix4x3" > - <inject-code class="target" position="beginning"> - <insert-template name="inplace_sub"/> - </inject-code> - </add-function> - <add-function signature="operator/=(float)" return-type="QMatrix4x3" > - <inject-code class="target" position="beginning"> - <insert-template name="inplace_div"/> - </inject-code> - </add-function> - <add-function signature="operator==(const QMatrix4x3&)" return-type="bool" /> </value-type> <value-type name="QMatrix4x4" since="4.6"> - <!-- Qt5: HAIRY TRICK ALERT ahead! Qt5 partially replaced 'qreal' by float. That had the side effect that all matrix types did not work any longer. @@ -2977,10 +2694,10 @@ The signature "QList<qreal>" is needed by the __reduce__ methods, but created by some other object used elsewhere. - After the matrix type was changed, "QList<float>" was nowhere created. + After the matrix type was changed, "QList<float>" was created nowhere. I don't know an explicit way to produce the right conversion function, so what I did - was to create a dummy function and immediately dele it again. + was to create a dummy function and immediately delete it again. This has the desired effect of creating the implicitly needed "QList<float>" conversion, although the dummy function goes away. @@ -3266,12 +2983,29 @@ </extra-includes> </primitive-type> - <object-type name="QWindow"> + <object-type name="QWindow" delete-in-main-thread="true"> <enum-type name="AncestorMode"/> <enum-type name="Visibility"/> + <modify-function signature="raise()" rename="raise_" /> + <!-- see QWidget::nativeEvent(), QAbstractNativeEventFilter::nativeEventFilter() --> + <modify-function signature="nativeEvent(const QByteArray &,void*,long*)"> + <modify-argument index="3"> + <remove-argument/> + <conversion-rule class="native"> + <insert-template name="return_native_eventfilter_conversion_variables"/> + </conversion-rule> + </modify-argument> + <modify-argument index="return"> + <replace-type modified-type="PyObject"/> + <conversion-rule class="native"> + <insert-template name="return_native_eventfilter_conversion"/> + </conversion-rule> + </modify-argument> + <inject-code position="end"> + <insert-template name="return_native_eventfilter"/> + </inject-code> + </modify-function> </object-type> - <!-- Qt5: not sure if this needs support, skipped for now --> - <rejection class="QWindow" function-name="nativeEvent"/>" <object-type name="QGuiApplication"> <extra-includes> @@ -3296,7 +3030,6 @@ } </inject-code> </add-function> - <modify-function signature="exec()" rename="exec_" allow-thread="yes"/> <inject-code class="native" file="glue/qguiapplication_init.cpp" position="beginning" /> </object-type> diff --git a/sources/pyside2/PySide2/QtNetwork/CMakeLists.txt b/sources/pyside2/PySide2/QtNetwork/CMakeLists.txt index 0267bfae3..a7c8dd8f6 100644 --- a/sources/pyside2/PySide2/QtNetwork/CMakeLists.txt +++ b/sources/pyside2/PySide2/QtNetwork/CMakeLists.txt @@ -1,23 +1,7 @@ project(QtNetwork) -set(QtNetwork_OPTIONAL_SRC ) set(QtNetwork_DROPPED_ENTRIES ) -check_qt_class(QtNetwork QSslCertificate QtNetwork_OPTIONAL_SRC QtNetwork_DROPPED_ENTRIES) -check_qt_class(QtNetwork QSslCertificateExtension QtNetwork_OPTIONAL_SRC QtNetwork_DROPPED_ENTRIES) -check_qt_class(QtNetwork QSslCipher QtNetwork_OPTIONAL_SRC QtNetwork_DROPPED_ENTRIES) -check_qt_class(QtNetwork QSslConfiguration QtNetwork_OPTIONAL_SRC QtNetwork_DROPPED_ENTRIES) -check_qt_class(QtNetwork QSslDiffieHellmanParameters QtNetwork_OPTIONAL_SRC QtNetwork_DROPPED_ENTRIES) -# Problems with operator==(QSslEllipticCurve,QSslEllipticCurve) -# check_qt_class(QtNetwork QSslEllipticCurve QtNetwork_OPTIONAL_SRC QtNetwork_DROPPED_ENTRIES) -check_qt_class(QtNetwork QSslError QtNetwork_OPTIONAL_SRC QtNetwork_DROPPED_ENTRIES) -check_qt_class(QtNetwork QSslKey QtNetwork_OPTIONAL_SRC QtNetwork_DROPPED_ENTRIES) -check_qt_class(QtNetwork QSslPreSharedKeyAuthenticator QtNetwork_OPTIONAL_SRC QtNetwork_DROPPED_ENTRIES) -check_qt_class(QtNetwork QSslSocket QtNetwork_OPTIONAL_SRC QtNetwork_DROPPED_ENTRIES) - -check_qt_class(QtNetwork QSctpServer QtNetwork_OPTIONAL_SRC QtNetwork_DROPPED_ENTRIES) -check_qt_class(QtNetwork QSctpSocket QtNetwork_OPTIONAL_SRC QtNetwork_DROPPED_ENTRIES) - set(QtNetwork_SRC ${QtNetwork_GEN_DIR}/qabstractnetworkcache_wrapper.cpp ${QtNetwork_GEN_DIR}/qabstractsocket_wrapper.cpp @@ -52,15 +36,63 @@ ${QtNetwork_GEN_DIR}/qnetworkproxyquery_wrapper.cpp ${QtNetwork_GEN_DIR}/qnetworkreply_wrapper.cpp ${QtNetwork_GEN_DIR}/qnetworkrequest_wrapper.cpp ${QtNetwork_GEN_DIR}/qnetworksession_wrapper.cpp +${QtNetwork_GEN_DIR}/qpassworddigestor_wrapper.cpp ${QtNetwork_GEN_DIR}/qssl_wrapper.cpp ${QtNetwork_GEN_DIR}/qtcpserver_wrapper.cpp ${QtNetwork_GEN_DIR}/qtcpsocket_wrapper.cpp ${QtNetwork_GEN_DIR}/qudpsocket_wrapper.cpp -${QtNetwork_OPTIONAL_SRC} # module is always needed ${QtNetwork_GEN_DIR}/qtnetwork_module_wrapper.cpp ) +get_property(QtNetwork_enabled_features TARGET Qt5::Network PROPERTY INTERFACE_QT_ENABLED_FEATURES) +get_property(QtNetwork_disabled_features TARGET Qt5::Network PROPERTY INTERFACE_QT_DISABLED_FEATURES) + +# ### fixme: For cmake >= 3.3, use if( needle IN_LIST list) +list(FIND QtNetwork_enabled_features "ssl" _sslEnabledIndex) +list(FIND QtNetwork_disabled_features "dtls" _dtlsDisabledIndex) +list(FIND QtNetwork_disabled_features "sctp" _sctpDisabledIndex) + +if(_sslEnabledIndex EQUAL -1) + list(APPEND QtNetwork_DROPPED_ENTRIES QSslCertificate QSslCertificateExtension + QSslCipher QSslConfiguration QSslDiffieHellmanParameters QSslError + QSslKey QSslPreSharedKeyAuthenticator QSslSocket) + message(STATUS "Qt5Network: Dropping SSL classes") +else() + # Problems with operator==(QSslEllipticCurve,QSslEllipticCurve) + # check_qt_class(QtNetwork QSslEllipticCurve QtNetwork_OPTIONAL_SRC QtNetwork_DROPPED_ENTRIES) + list(APPEND QtNetwork_SRC + ${QtNetwork_GEN_DIR}/qsslcertificate_wrapper.cpp + ${QtNetwork_GEN_DIR}/qsslcertificateextension_wrapper.cpp + ${QtNetwork_GEN_DIR}/qsslcipher_wrapper.cpp + ${QtNetwork_GEN_DIR}/qsslconfiguration_wrapper.cpp + ${QtNetwork_GEN_DIR}/qssldiffiehellmanparameters_wrapper.cpp + ${QtNetwork_GEN_DIR}/qsslerror_wrapper.cpp + ${QtNetwork_GEN_DIR}/qsslkey_wrapper.cpp + ${QtNetwork_GEN_DIR}/qsslpresharedkeyauthenticator_wrapper.cpp + ${QtNetwork_GEN_DIR}/qsslsocket_wrapper.cpp) + message(STATUS "Qt5Network: Adding SSL classes") +endif() + +if(_dtlsDisabledIndex GREATER -1) + list(APPEND QtNetwork_DROPPED_ENTRIES QDtls) + message(STATUS "Qt5Network: Dropping DTLS classes") +else() + list(APPEND QtNetwork_SRC + ${QtNetwork_GEN_DIR}/qdtls_wrapper.cpp) + message(STATUS "Qt5Network: Adding DTLS classes") +endif() + +if(_sctpDisabledIndex GREATER -1) + list(APPEND QtNetwork_DROPPED_ENTRIES QSctpServer QSctpSocket) + message(STATUS "Qt5Network: Dropping SCTP classes") +else() + list(APPEND QtNetwork_SRC + ${QtNetwork_GEN_DIR}/qsctpserver_wrapper.cpp + ${QtNetwork_GEN_DIR}/qsctpsocket_wrapper.cpp) + message(STATUS "Qt5Network: Adding SCTP classes") +endif() + set(QtNetwork_include_dirs ${QtNetwork_SOURCE_DIR} ${QtNetwork_BINARY_DIR} ${Qt5Core_INCLUDE_DIRS} diff --git a/sources/pyside2/PySide2/QtNetwork/typesystem_network.xml b/sources/pyside2/PySide2/QtNetwork/typesystem_network.xml index e4235e070..d277b3228 100644 --- a/sources/pyside2/PySide2/QtNetwork/typesystem_network.xml +++ b/sources/pyside2/PySide2/QtNetwork/typesystem_network.xml @@ -42,6 +42,14 @@ <typesystem package="PySide2.QtNetwork"> <load-typesystem name="QtCore/typesystem_core.xml" generate="no"/> + <enum-type name="QDtlsError" since="5.12"/> + + <namespace-type name="QPasswordDigestor" since="5.12"> + <extra-includes> + <include file-name="qpassworddigestor.h" location="global"/> + </extra-includes> + </namespace-type> + <namespace-type name="QSsl"> <enum-type name="AlternativeNameEntryType"/> <enum-type name="EncodingFormat"/> @@ -80,6 +88,11 @@ <value-type name="QDnsMailExchangeRecord"/> <value-type name="QDnsServiceRecord"/> <value-type name="QDnsTextRecord"/> + + <object-type name="QDtls" since="5.12"> + <enum-type name="HandshakeState"/> + </object-type> + <value-type name="QHstsPolicy" since="5.9"> <enum-type name="PolicyFlag" flags="PolicyFlags"/> </value-type> @@ -275,7 +288,7 @@ </extra-includes> </value-type> <value-type name="QNetworkRequest"> - <enum-type name="Attribute" extensible="yes"/> + <enum-type name="Attribute"/> <enum-type name="LoadControl" since="4.7"/> <enum-type name="Priority" since="4.7"/> <enum-type name="CacheLoadControl"/> diff --git a/sources/pyside2/PySide2/QtOpenGL/typesystem_opengl.xml b/sources/pyside2/PySide2/QtOpenGL/typesystem_opengl.xml index ea5c24cd3..c332eea0d 100644 --- a/sources/pyside2/PySide2/QtOpenGL/typesystem_opengl.xml +++ b/sources/pyside2/PySide2/QtOpenGL/typesystem_opengl.xml @@ -59,19 +59,19 @@ <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="^(const )?GLboolean ?\*$"/> + <rejection class="^QGL.*$" argument-type="^(const )?GLchar ?\*$"/> + <rejection class="^QGL.*$" argument-type="^(const )?GLchar ?\*const"/> + <rejection class="^QGL.*$" argument-type="^(const )?GLenum ?\*$"/> + <rejection class="^QGL.*$" argument-type="^(const )?GLfloat ?\*$"/> + <rejection class="^QGL.*$" argument-type="^(const )?GLfloat ?\[.*$"/> + <rejection class="^QGL.*$" argument-type="^(const )?GLdouble ?\*$"/> <rejection class="^QGL.*$" argument-type="GLintptr"/> - <rejection class="^QGL.*$" argument-type="^GLint64( const)?\*$"/> - <rejection class="^QGL.*$" argument-type="^GLsizei( const)?\*$"/> + <rejection class="^QGL.*$" argument-type="^(const )?GLint64 ?\*$"/> + <rejection class="^QGL.*$" argument-type="^(const )?GLsizei ?\*$"/> <namespace-type name="QGL"> - <enum-type name="FormatOption" flags="FormatOptions" force-integer="yes"/> + <enum-type name="FormatOption" flags="FormatOptions"/> <extra-includes> <include file-name="qgl.h" location="global"/> </extra-includes> @@ -683,16 +683,7 @@ </inject-code> </modify-function> - <!-- ### TODO: must evaluate if anything other than removal should be done. --> - <modify-function signature="setAttributeArray(int,const GLfloat*,int,int)" remove="all" /> - <modify-function signature="setAttributeArray(const char*,const GLfloat*,int,int)" remove="all" /> - <modify-function signature="setUniformValueArray(int,const GLfloat*,int,int)" remove="all" /> - <modify-function signature="setUniformValueArray(const char*,const GLfloat*,int,int)" remove="all" /> - <!-- ### --> - <!-- ### Use QMatrixZxY overloads --> - <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" /> <modify-function signature="setAttributeArray(const char*,GLenum,const void*,int,int)" remove="all" since="4.7" /> <!-- ### --> diff --git a/sources/pyside2/PySide2/QtQml/pysideqmlregistertype.cpp b/sources/pyside2/PySide2/QtQml/pysideqmlregistertype.cpp index 1a1f09511..9d9ddc799 100644 --- a/sources/pyside2/PySide2/QtQml/pysideqmlregistertype.cpp +++ b/sources/pyside2/PySide2/QtQml/pysideqmlregistertype.cpp @@ -44,6 +44,7 @@ // pyside #include <pyside.h> +#include <pyside_p.h> #include <pysideproperty.h> // auto generated headers @@ -126,8 +127,7 @@ int PySide::qmlRegisterType(PyObject *pyObj, const char *uri, int versionMajor, return -1; } - QMetaObject *metaObject = reinterpret_cast<QMetaObject *>( - ObjectType::getTypeUserData(reinterpret_cast<SbkObjectType *>(pyObj))); + const QMetaObject *metaObject = PySide::retrieveMetaObject(pyObjType); Q_ASSERT(metaObject); QQmlPrivate::RegisterType type; @@ -172,13 +172,13 @@ int PySide::qmlRegisterType(PyObject *pyObj, const char *uri, int versionMajor, type.versionMajor = versionMajor; type.versionMinor = versionMinor; type.elementName = qmlName; - type.metaObject = metaObject; type.extensionObjectCreate = 0; type.extensionMetaObject = 0; type.customParser = 0; ++nextType; } + type.metaObject = metaObject; // Snapshot may have changed. int qmlTypeId = QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type); if (qmlTypeId == -1) { diff --git a/sources/pyside2/PySide2/QtQml/typesystem_qml.xml b/sources/pyside2/PySide2/QtQml/typesystem_qml.xml index 9a0d1e0ae..f5e3d36fc 100644 --- a/sources/pyside2/PySide2/QtQml/typesystem_qml.xml +++ b/sources/pyside2/PySide2/QtQml/typesystem_qml.xml @@ -157,7 +157,7 @@ <enum-type name="Status" /> </object-type> <object-type name="QQmlIncubationController"> - <modify-function signature="incubateWhile(bool volatile*,int)" allow-thread="yes"> + <modify-function signature="incubateWhile(volatile bool*,int)" allow-thread="yes"> <modify-argument index="1"> <!-- The replace type is needed to use the VolatileBool_Check macro instead of a template conversion function with "volatile bool" as argument. --> @@ -216,5 +216,5 @@ </modify-function> </object-type> <!-- Suppress anonymous enum warning --> - <suppress-warning text="enum 'QmlCurrentSingletonTypeRegistrationVersion' does not have a type entry or is not an enum" /> + <suppress-warning text="Anonymous enum (QmlCurrentSingletonTypeRegistrationVersion) does not have a type entry"/> </typesystem> diff --git a/sources/pyside2/PySide2/QtQuick/pysidequickregistertype.cpp b/sources/pyside2/PySide2/QtQuick/pysidequickregistertype.cpp index bf3ff06a2..93a8f281e 100644 --- a/sources/pyside2/PySide2/QtQuick/pysidequickregistertype.cpp +++ b/sources/pyside2/PySide2/QtQuick/pysidequickregistertype.cpp @@ -40,6 +40,7 @@ #include "pysidequickregistertype.h" #include <pyside.h> +#include <shiboken.h> // Auto generated headers. #include "qquickitem_wrapper.h" diff --git a/sources/pyside2/PySide2/QtQuick/typesystem_quick.xml b/sources/pyside2/PySide2/QtQuick/typesystem_quick.xml index 7e6b450c9..dde90166f 100644 --- a/sources/pyside2/PySide2/QtQuick/typesystem_quick.xml +++ b/sources/pyside2/PySide2/QtQuick/typesystem_quick.xml @@ -65,7 +65,7 @@ <object-type name="QQuickImageResponse" since="5.6" /> <object-type name="QQuickTransform" /> - <object-type name="QQuickItem"> + <object-type name="QQuickItem" delete-in-main-thread="true"> <value-type name="UpdatePaintNodeData" /> <enum-type name="Flag" flags="Flags" /> <enum-type name="ItemChange" /> diff --git a/sources/pyside2/PySide2/QtSql/typesystem_sql.xml b/sources/pyside2/PySide2/QtSql/typesystem_sql.xml index 1b6baa15f..3eab73307 100644 --- a/sources/pyside2/PySide2/QtSql/typesystem_sql.xml +++ b/sources/pyside2/PySide2/QtSql/typesystem_sql.xml @@ -101,7 +101,6 @@ <extra-includes> <include file-name="QSqlField" location="global"/> </extra-includes> - <modify-function signature="append(QSqlField)" access="non-final"/> </value-type> <value-type name="QSqlError"> @@ -142,8 +141,6 @@ </object-type> <object-type name="QSqlQueryModel"> - <modify-function signature="indexInQuery(QModelIndex)const" access="non-final"/> - <modify-function signature="setQuery(QSqlQuery)" access="non-final"/> <extra-includes> <include file-name="QSqlError" location="global"/> <include file-name="QSqlQuery" location="global"/> diff --git a/sources/pyside2/PySide2/QtWidgets/CMakeLists.txt b/sources/pyside2/PySide2/QtWidgets/CMakeLists.txt index dee79744f..6b8830a41 100644 --- a/sources/pyside2/PySide2/QtWidgets/CMakeLists.txt +++ b/sources/pyside2/PySide2/QtWidgets/CMakeLists.txt @@ -1,12 +1,5 @@ project(QtWidgets) - -set(QtWidgets_OPTIONAL_SRC ) -set(QtWidgets_DROPPED_ENTRIES ) -## XXX check if these conditionals need to be done elsewhere -check_qt_class(QtWidgets QGtkStyle QtWidgets_OPTIONAL_SRC QtWidgets_DROPPED_ENTRIES) -check_qt_class(QtWidgets QMacStyle QtWidgets_OPTIONAL_SRC QtWidgets_DROPPED_ENTRIES) - set(QtWidgets_SRC ${QtWidgets_GEN_DIR}/qaccessiblewidget_wrapper.cpp ${QtWidgets_GEN_DIR}/qabstractbutton_wrapper.cpp diff --git a/sources/pyside2/PySide2/QtWidgets/typesystem_widgets_common.xml b/sources/pyside2/PySide2/QtWidgets/typesystem_widgets_common.xml index 76b3dd1f1..d92540d85 100644 --- a/sources/pyside2/PySide2/QtWidgets/typesystem_widgets_common.xml +++ b/sources/pyside2/PySide2/QtWidgets/typesystem_widgets_common.xml @@ -74,7 +74,7 @@ --> <object-type name="QStyleOption" polymorphic-id-expression="%1->type == QStyleOption::SO_Default"> - <enum-type name="OptionType" extensible="yes"/> + <enum-type name="OptionType"/> <enum-type name="StyleOptionType"/> <enum-type name="StyleOptionVersion"/> </object-type> @@ -1272,17 +1272,17 @@ </object-type> <object-type name="QAbstractButton"/> <object-type name="QStyle"> - <enum-type name="ComplexControl" extensible="yes"/> - <enum-type name="ContentsType" extensible="yes"/> - <enum-type name="ControlElement" extensible="yes"/> - <enum-type name="PixelMetric" extensible="yes" /> - <enum-type name="PrimitiveElement" extensible="yes" /> + <enum-type name="ComplexControl"/> + <enum-type name="ContentsType"/> + <enum-type name="ControlElement"/> + <enum-type name="PixelMetric"/> + <enum-type name="PrimitiveElement"/> <enum-type name="RequestSoftwareInputPanel" since="4.6"/> - <enum-type name="StandardPixmap" extensible="yes"/> + <enum-type name="StandardPixmap"/> <enum-type name="StateFlag" flags="State"/> - <enum-type name="StyleHint" extensible="yes" /> - <enum-type name="SubControl" flags="SubControls" extensible="yes" force-integer="yes"/> - <enum-type name="SubElement" extensible="yes" /> + <enum-type name="StyleHint"/> + <enum-type name="SubControl" flags="SubControls"/> + <enum-type name="SubElement"/> <modify-function signature="drawComplexControl(QStyle::ComplexControl,const QStyleOptionComplex*,QPainter*,const QWidget*)const"> <modify-argument index="3" invalidate-after-use="yes"/> <modify-argument index="4"> @@ -1579,7 +1579,6 @@ <enum-type name="OptimizationFlag" flags="OptimizationFlags"/> <enum-type name="ViewportAnchor"/> <enum-type name="ViewportUpdateMode"/> - <modify-function signature="setupViewport(QWidget*)" access="non-final"/> <modify-function signature="setScene(QGraphicsScene*)"> <modify-argument index="1"> <reference-count action="set"/> @@ -1876,13 +1875,6 @@ </inject-code> </modify-function> - <!-- use glue code --> - <modify-function signature="drawItems(QPainter*,int,QGraphicsItem**,const QStyleOptionGraphicsItem*,QWidget*)"> - <modify-argument index="2"> - <remove-argument/> - </modify-argument> - </modify-function> - <modify-function signature="clear()"> <inject-code> const QList<QGraphicsItem*> items = %CPPSELF.items(); @@ -2204,9 +2196,25 @@ </modify-function> </object-type> - <object-type name="QWidget"> - <!-- Qt5: remove native event for now --> - <modify-function signature="nativeEvent(const QByteArray &,void*,long*)" remove="all" /> + <object-type name="QWidget" delete-in-main-thread="true"> + <!-- see QWindow::nativeEvent(), QAbstractNativeEventFilter::nativeEventFilter() --> + <modify-function signature="nativeEvent(const QByteArray &,void*,long*)"> + <modify-argument index="3"> + <remove-argument/> + <conversion-rule class="native"> + <insert-template name="return_native_eventfilter_conversion_variables"/> + </conversion-rule> + </modify-argument> + <modify-argument index="return"> + <replace-type modified-type="PyObject"/> + <conversion-rule class="native"> + <insert-template name="return_native_eventfilter_conversion"/> + </conversion-rule> + </modify-argument> + <inject-code position="end"> + <insert-template name="return_native_eventfilter"/> + </inject-code> + </modify-function> <extra-includes> <include file-name="QIcon" location="global"/> @@ -2491,6 +2499,7 @@ <enum-type name="ButtonSymbols"/> <enum-type name="CorrectionMode"/> <enum-type name="StepEnabledFlag" flags="StepEnabled"/> + <enum-type name="StepType" since="5.12"/> <modify-function signature="setLineEdit(QLineEdit*)"> <modify-argument index="1"> <parent index="this" action="add"/> @@ -2968,7 +2977,7 @@ </object-type> <object-type name="QDesktopWidget"/> <object-type name="QFrame"> - <enum-type name="Shadow" extensible="yes"/> + <enum-type name="Shadow"/> <enum-type name="Shape"/> <enum-type name="StyleMask"/> </object-type> diff --git a/sources/pyside2/PySide2/QtXmlPatterns/typesystem_xmlpatterns.xml b/sources/pyside2/PySide2/QtXmlPatterns/typesystem_xmlpatterns.xml index f4e690874..2ac150807 100644 --- a/sources/pyside2/PySide2/QtXmlPatterns/typesystem_xmlpatterns.xml +++ b/sources/pyside2/PySide2/QtXmlPatterns/typesystem_xmlpatterns.xml @@ -101,7 +101,7 @@ <modify-function signature="kind()const" remove="all" /> <modify-function signature="isDeepEqual(const QXmlNodeModelIndex&)const" remove="all" /> <modify-function signature="compareOrder(const QXmlNodeModelIndex &)const" remove="all" /> - <modify-function signature="sendNamespaces(QAbstractXmlReceiver*)const" remove="all" /> + <modify-function signature="sendNamespaces(QAbstractXmlReceiver*const)const" remove="all" /> <modify-function signature="namespaceBindings()const" remove="all" /> <modify-function signature="namespaceForPrefix(QXmlName::PrefixCode)const" remove="all" /> <modify-function signature="stringValue()const" remove="all" /> diff --git a/sources/pyside2/PySide2/__init__.py.in b/sources/pyside2/PySide2/__init__.py.in index ab50ef776..631f5f13a 100644 --- a/sources/pyside2/PySide2/__init__.py.in +++ b/sources/pyside2/PySide2/__init__.py.in @@ -4,18 +4,22 @@ __all__ = list("Qt" + body for body in __version__ = "@FINAL_PACKAGE_VERSION@" __version_info__ = (@BINDING_API_MAJOR_VERSION@, @BINDING_API_MINOR_VERSION@, @BINDING_API_MICRO_VERSION@, "@BINDING_API_PRE_RELEASE_VERSION_TYPE@", "@BINDING_API_PRE_RELEASE_VERSION@") -@PYSIDE_BUILD_DATE@ -@PYSIDE_BUILD_COMMIT_DATE@ -@PYSIDE_BUILD_COMMIT_HASH@ -@PYSIDE_BUILD_COMMIT_HASH_DESCRIBED@ - -# Timestamp used for snapshot build, which is part of snapshot package version. -@PYSIDE_SETUP_PY_PACKAGE_TIMESTAMP_ASSIGNMENT@ - def _setupQtDirectories(): import sys import os + # On Windows we need to explicitly import the shiboken2 module so + # that the libshiboken.dll dependency is loaded by the time a + # Qt module is imported. Otherwise due to PATH not containing + # the shiboken2 module path, the Qt module import would fail + # due to the missing libshiboken dll. + # We need to do the same on Linux and macOS, because we do not + # embed rpaths into the PySide2 libraries that would point to + # the libshiboken library location. Importing the module + # loads the libraries into the process memory beforehand, and + # thus takes care of it for us. + import shiboken2 + pyside_package_dir = os.path.abspath(os.path.dirname(__file__)) # Used by signature module. os.environ["PYSIDE_PACKAGE_DIR"] = pyside_package_dir diff --git a/sources/pyside2/PySide2/_config.py.in b/sources/pyside2/PySide2/_config.py.in index 31a2f7a50..740e9a001 100644 --- a/sources/pyside2/PySide2/_config.py.in +++ b/sources/pyside2/PySide2/_config.py.in @@ -8,10 +8,9 @@ pyside_library_soversion = str(@PYSIDE_SO_VERSION@) version = "@FINAL_PACKAGE_VERSION@" version_info = (@BINDING_API_MAJOR_VERSION@, @BINDING_API_MINOR_VERSION@, @BINDING_API_MICRO_VERSION@, "@BINDING_API_PRE_RELEASE_VERSION_TYPE@", "@BINDING_API_PRE_RELEASE_VERSION@") -@PYSIDE_BUILD_DATE@ -@PYSIDE_BUILD_COMMIT_DATE@ -@PYSIDE_BUILD_COMMIT_HASH@ -@PYSIDE_BUILD_COMMIT_HASH_DESCRIBED@ - -# Timestamp used for snapshot build, which is part of snapshot package version. -@PYSIDE_SETUP_PY_PACKAGE_TIMESTAMP_ASSIGNMENT@ +@PACKAGE_BUILD_DATE@ +@PACKAGE_BUILD_COMMIT_DATE@ +@PACKAGE_BUILD_COMMIT_HASH@ +@PACKAGE_BUILD_COMMIT_HASH_DESCRIBED@ +@PACKAGE_SETUP_PY_PACKAGE_TIMESTAMP_ASSIGNMENT@ +@PACKAGE_SETUP_PY_PACKAGE_VERSION_ASSIGNMENT@ diff --git a/sources/pyside2/PySide2/support/signature/__init__.py b/sources/pyside2/PySide2/support/signature/__init__.py index 0ff9ec7e9..14e63a5fb 100644 --- a/sources/pyside2/PySide2/support/signature/__init__.py +++ b/sources/pyside2/PySide2/support/signature/__init__.py @@ -1,6 +1,6 @@ ############################################################################# ## -## Copyright (C) 2017 The Qt Company Ltd. +## Copyright (C) 2018 The Qt Company Ltd. ## Contact: https://www.qt.io/licensing/ ## ## This file is part of Qt for Python. @@ -40,3 +40,7 @@ from __future__ import print_function, absolute_import from .loader import inspect +from PySide2 import QtCore +if QtCore.QProcess.__signature__: + pass # trigger initialization +from signature_loader import get_signature diff --git a/sources/pyside2/PySide2/support/signature/fix-complaints.py b/sources/pyside2/PySide2/support/signature/fix-complaints.py index fa2b44420..e078ef1ab 100644 --- a/sources/pyside2/PySide2/support/signature/fix-complaints.py +++ b/sources/pyside2/PySide2/support/signature/fix-complaints.py @@ -1,6 +1,6 @@ ############################################################################# ## -## Copyright (C) 2017 The Qt Company Ltd. +## Copyright (C) 2018 The Qt Company Ltd. ## Contact: https://www.qt.io/licensing/ ## ## This file is part of Qt for Python. diff --git a/sources/pyside2/PySide2/support/signature/layout.py b/sources/pyside2/PySide2/support/signature/layout.py new file mode 100644 index 000000000..ac7833f03 --- /dev/null +++ b/sources/pyside2/PySide2/support/signature/layout.py @@ -0,0 +1,237 @@ +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of Qt for Python. +## +## $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$ +## +############################################################################# + +from __future__ import print_function, absolute_import + +""" +layout.py + +The signature module now has the capability to configure +differently formatted versions of signatures. The default +layout is known from the "__signature__" attribute. + +The function "get_signature(ob, modifier=None)" produces the same +signatures by default. By passing different modifiers, you +can select different layouts. + +This module configures the different layouts which can be used. +It also implements them in this file. The configurations are +used literally as strings like "signature", "existence", etc. +""" + +from textwrap import dedent +from .loader import inspect + +class SimpleNamespace(object): + # From types.rst, because the builtin is implemented in Python 3, only. + def __init__(self, **kwargs): + self.__dict__.update(kwargs) + + def __repr__(self): + keys = sorted(self.__dict__) + items = ("{}={!r}".format(k, self.__dict__[k]) for k in keys) + return "{}({})".format(type(self).__name__, ", ".join(items)) + + def __eq__(self, other): + return self.__dict__ == other.__dict__ + +class SignatureLayout(SimpleNamespace): + """ + Configure a signature. + + The layout of signatures can have different layouts which are + controlled by keyword arguments: + + definition=True Determines if self will generated. + defaults=True + ellipsis=False Replaces defaults by "...". + return_annotation=True + parameter_names=True False removes names before ":". + """ + allowed_keys = SimpleNamespace(definition=True, + defaults=True, + ellipsis=False, + return_annotation=True, + parameter_names=True) + allowed_values = True, False + + def __init__(self, **kwds): + args = SimpleNamespace(**self.allowed_keys.__dict__) + args.__dict__.update(kwds) + self.__dict__.update(args.__dict__) + err_keys = list(set(self.__dict__) - set(self.allowed_keys.__dict__)) + if err_keys: + self._attributeerror(err_keys) + err_values = list(set(self.__dict__.values()) - set(self.allowed_values)) + if err_values: + self._valueerror(err_values) + + def __setattr__(self, key, value): + if key not in self.allowed_keys.__dict__: + self._attributeerror([key]) + if value not in self.allowed_values: + self._valueerror([value]) + self.__dict__[key] = value + + def _attributeerror(self, err_keys): + err_keys = ", ".join(err_keys) + allowed_keys = ", ".join(self.allowed_keys.__dict__.keys()) + raise AttributeError(dedent("""\ + Not allowed: '{err_keys}'. + The only allowed keywords are '{allowed_keys}'. + """.format(**locals()))) + + def _valueerror(self, err_values): + err_values = ", ".join(map(str, err_values)) + allowed_values = ", ".join(map(str, self.allowed_values)) + raise ValueError(dedent("""\ + Not allowed: '{err_values}'. + The only allowed values are '{allowed_values}'. + """.format(**locals()))) + +# The following names are used literally in this module. +# This way, we avoid the dict hashing problem. +signature = SignatureLayout() + +existence = SignatureLayout(definition=False, + defaults=False, + return_annotation=False, + parameter_names=False) + +hintingstub = SignatureLayout(ellipsis=True) + +typeerror = SignatureLayout(definition=False, + return_annotation=False, + parameter_names=False) + +def define_nameless_parameter(): + """ + Create Nameless Parameters + + A nameless parameter has a reduced string representation. + This is done by cloning the parameter type and overwriting its + __str__ method. The inner structure is still a valid parameter. + """ + def __str__(self): + # for Python 2, we must change self to be an instance of P + klass = self.__class__ + self.__class__ = P + txt = P.__str__(self) + self.__class__ = klass + txt = txt[txt.index(":") + 1:].strip() if ":" in txt else txt + return txt + + P = inspect.Parameter + newname = "NamelessParameter" + bases = P.__bases__ + body = dict(P.__dict__) # get rid of mappingproxy + if "__slots__" in body: + # __slots__ would create duplicates + for name in body["__slots__"]: + del body[name] + body["__str__"] = __str__ + return type(newname, bases, body) + +NamelessParameter = define_nameless_parameter() + +def make_signature_nameless(signature): + """ + Make a Signature Nameless + + We use an existing signature and change the type of its parameters. + The signature looks different, but is totally intact. + """ + for key in signature.parameters.keys(): + Signature.parameters[key].__class__ = NamelessParameter + +def create_signature(props, key): + if not props: + # empty signatures string + return + if isinstance(props["multi"], list): + # multi sig: call recursively + return list(create_signature(elem, key) + for elem in props["multi"]) + if type(key) is tuple: + sig_kind, modifier = key + else: + sig_kind, modifier = key, "signature" + + layout = globals()[modifier] # lookup of the modifier, here + if not isinstance(layout, SignatureLayout): + raise SystemError("Modifiers must be names of a SignatureLayout " + "instance") + + # this is the basic layout of a signature + varnames = props["varnames"] + if layout.definition: + if sig_kind == "method": + varnames = ("self",) + varnames + elif sig_kind == "staticmethod": + pass + elif sig_kind == "classmethod": + varnames = ("klass",) + varnames + else: + raise SystemError("Methods must be normal, staticmethod or " + "classmethod") + # calculate the modifications + defaults = props["defaults"][:] + if not layout.defaults: + defaults = () + if layout.ellipsis: + defaults = ("...",) * len(defaults) + annotations = props["annotations"].copy() + if not layout.return_annotation and "return" in annotations: + del annotations["return"] + + # attach parameters to a fake function and build a signature + argstr = ", ".join(varnames) + fakefunc = eval("lambda {}: None".format(argstr)) + fakefunc.__name__ = props["name"] + fakefunc.__defaults__ = defaults + fakefunc.__kwdefaults__ = props["kwdefaults"] + fakefunc.__annotations__ = annotations + sig = inspect._signature_from_function(inspect.Signature, fakefunc) + + # the special case of nameless parameters + if not layout.parameter_names: + make_signature_nameless(sig) + return sig + +# end of file diff --git a/sources/pyside2/PySide2/support/signature/lib/__init__.py b/sources/pyside2/PySide2/support/signature/lib/__init__.py new file mode 100644 index 000000000..2d640cb89 --- /dev/null +++ b/sources/pyside2/PySide2/support/signature/lib/__init__.py @@ -0,0 +1,40 @@ +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of Qt for Python. +## +## $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$ +## +############################################################################# + +# this file intentionally left blank diff --git a/sources/pyside2/PySide2/support/signature/lib/enum_sig.py b/sources/pyside2/PySide2/support/signature/lib/enum_sig.py new file mode 100644 index 000000000..702ee7ebd --- /dev/null +++ b/sources/pyside2/PySide2/support/signature/lib/enum_sig.py @@ -0,0 +1,113 @@ +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of Qt for Python. +## +## $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$ +## +############################################################################# + +import sys +from PySide2.support.signature import inspect, get_signature + + +class ExactEnumerator(object): + """ + ExactEnumerator enumerates all signatures in a module as they are. + + This class is used for generating complete listings of all signatures. + An appropriate formatter should be supplied, if printable output + is desired. + """ + def __init__(self, formatter, result_type=dict): + self.fmt = formatter + self.result_type = result_type + + def module(self, mod_name): + __import__(mod_name) + with self.fmt.module(mod_name): + module = sys.modules[mod_name] + members = inspect.getmembers(module, inspect.isclass) + ret = self.result_type() + for class_name, klass in members: + ret.update(self.klass(class_name, klass)) + return ret + + def klass(self, class_name, klass): + bases_list = [] + for base in klass.__bases__: + name = base.__name__ + if name == "object": + pass + else: + modname = base.__module__ + name = modname + "." + base.__name__ + bases_list.append(name) + class_str = "{}({})".format(class_name, ", ".join(bases_list)) + with self.fmt.klass(class_name, class_str): + ret = self.function("__init__", klass) + # class_members = inspect.getmembers(klass) + # gives us also the inherited things. + class_members = sorted(list(klass.__dict__.items())) + for func_name, func in class_members: + ret.update(self.function(func_name, func)) + return ret + + def function(self, func_name, func): + ret = self.result_type() + signature = getattr(func, '__signature__', None) + if signature is not None: + with self.fmt.function(func_name, signature) as key: + ret[key] = signature + return ret + + +class SimplifyingEnumerator(ExactEnumerator): + """ + SimplifyingEnumerator enumerates all signatures in a module filtered. + + There are no default values, no variable + names and no self parameter. Only types are present after simplification. + The functions 'next' resp. '__next__' are removed + to make the output identical for Python 2 and 3. + An appropriate formatter should be supplied, if printable output + is desired. + """ + + def function(self, func_name, func): + ret = self.result_type() + signature = get_signature(func, 'existence') + if signature is not None and func_name not in ("next", "__next__"): + with self.fmt.function(func_name, signature) as key: + ret[key] = signature + return ret diff --git a/sources/pyside2/PySide2/support/signature/loader.py b/sources/pyside2/PySide2/support/signature/loader.py index f51bafe79..21ecebcc8 100644 --- a/sources/pyside2/PySide2/support/signature/loader.py +++ b/sources/pyside2/PySide2/support/signature/loader.py @@ -1,6 +1,6 @@ ############################################################################# ## -## Copyright (C) 2017 The Qt Company Ltd. +## Copyright (C) 2018 The Qt Company Ltd. ## Contact: https://www.qt.io/licensing/ ## ## This file is part of Qt for Python. @@ -80,31 +80,10 @@ from PySide2.support.signature.parser import pyside_type_init sys.path.pop(0) # Note also that during the tests we have a different encoding that would # break the Python license decorated files without an encoding line. +from PySide2.support.signature import layout # name used in signature.cpp -def create_signature(props, sig_kind): - if not props: - # empty signatures string - return - if isinstance(props["multi"], list): - return list(create_signature(elem, sig_kind) - for elem in props["multi"]) - varnames = props["varnames"] - if sig_kind == "method": - varnames = ("self",) + varnames - elif sig_kind == "staticmethod": - pass - elif sig_kind == "classmethod": - varnames = ("klass",) + varnames - else: - raise SystemError("Methods must be normal, staticmethod or " - "classmethod") - argstr = ", ".join(varnames) - fakefunc = eval("lambda {}: None".format(argstr)) - fakefunc.__name__ = props["name"] - fakefunc.__defaults__ = props["defaults"] - fakefunc.__kwdefaults__ = props["kwdefaults"] - fakefunc.__annotations__ = props["annotations"] - return inspect._signature_from_function(inspect.Signature, fakefunc) +def create_signature(props, key): + return layout.create_signature(props, key) # end of file diff --git a/sources/pyside2/PySide2/support/signature/mapping.py b/sources/pyside2/PySide2/support/signature/mapping.py index dd3df0988..6b7d1ad01 100644 --- a/sources/pyside2/PySide2/support/signature/mapping.py +++ b/sources/pyside2/PySide2/support/signature/mapping.py @@ -1,6 +1,6 @@ ############################################################################# ## -## Copyright (C) 2017 The Qt Company Ltd. +## Copyright (C) 2018 The Qt Company Ltd. ## Contact: https://www.qt.io/licensing/ ## ## This file is part of Qt for Python. @@ -46,7 +46,7 @@ This module has the mapping from the pyside C-modules view of signatures to the Python representation. The PySide modules are not loaded in advance, but only after they appear -in sys.modules. This minimises the loading overhead. +in sys.modules. This minimizes the loading overhead. In principle, we need to re-load the module, when the imports change. But it is much easier to do it on demand, when we get an exception. See _resolve_value() in singature.py @@ -71,7 +71,6 @@ FloatMatrix = typing.List[typing.List[float]] # Pair could be more specific, but we loose the info in the generator. Pair = typing.Tuple[typing.Any, typing.Any] MultiMap = typing.DefaultDict[str, typing.List[str]] -Text = typing.Text # ulong_max is only 32 bit on windows. ulong_max = 2*sys.maxsize+1 if len(struct.pack("L", 1)) != 4 else 0xffffffff @@ -153,7 +152,7 @@ type_map = {} def init_QtCore(): import PySide2.QtCore - from PySide2.QtCore import Qt, QUrl, QDir, QGenericArgument + from PySide2.QtCore import Qt, QUrl, QDir from PySide2.QtCore import QRect, QSize, QPoint, QLocale, QByteArray from PySide2.QtCore import QMarginsF # 5.9 try: @@ -201,9 +200,8 @@ def init_QtCore(): "ULONG_MAX": ulong_max, "quintptr": int, "PyCallable": typing.Callable, - "...": ellipsis, # no idea how this should be translated... maybe so? "PyTypeObject": type, - "PySequence": typing.Sequence, + "PySequence": typing.Iterable, # important for numpy "qptrdiff": int, "true": True, "Qt.HANDLE": int, # be more explicit with some consts? @@ -242,7 +240,7 @@ def init_QtCore(): "QDir.SortFlags(QDir.Name | QDir.IgnoreCase)"), "PyBytes": bytes, "PyByteArray": bytearray, - "PyUnicode": Text, + "PyUnicode": typing.Text, "signed long": int, "PySide2.QtCore.int": int, "PySide2.QtCore.char": StringList, # A 'char **' is a list of strings. @@ -259,13 +257,13 @@ def init_QtCore(): "float[][]": FloatMatrix, # 5.9 "PySide2.QtCore.unsigned int": int, # 5.9 Ubuntu "PySide2.QtCore.long long": int, # 5.9, MSVC 15 - "QGenericArgument(nullptr)": QGenericArgument(None), # 5.10 + "QGenericArgument(nullptr)": ellipsis, # 5.10 "QModelIndex()": Invalid("PySide2.QtCore.QModelIndex"), # repr is btw. very wrong, fix it?! - "QGenericArgument((0))": None, # 5.6, RHEL 6.6. Is that ok? - "QGenericArgument()": None, - "QGenericArgument(0)": None, - "QGenericArgument(NULL)": None, # 5.6, MSVC - "QGenericArgument(Q_NULLPTR)": None, + "QGenericArgument((0))": ellipsis, # 5.6, RHEL 6.6. Is that ok? + "QGenericArgument()": ellipsis, + "QGenericArgument(0)": ellipsis, + "QGenericArgument(NULL)": ellipsis, # 5.6, MSVC + "QGenericArgument(Q_NULLPTR)": ellipsis, "zero(PySide2.QtCore.QObject)": None, "zero(PySide2.QtCore.QThread)": None, "zero(quintptr)": 0, @@ -289,6 +287,8 @@ def init_QtCore(): "zero(PySide2.QtCore.QEvent.Type)": None, "CheckIndexOption.NoOption": Instance( "PySide2.QtCore.QAbstractItemModel.CheckIndexOptions.NoOption"), # 5.11 + "QVariantMap": dict, + "PySide2.QtCore.QCborStreamReader.StringResult": typing.AnyStr, }) try: type_map.update({ @@ -301,7 +301,6 @@ def init_QtCore(): def init_QtGui(): import PySide2.QtGui - from PySide2.QtGui import QPageLayout, QPageSize # 5.9 type_map.update({ "QVector< QTextLayout.FormatRange >()": [], # do we need more structure? "USHRT_MAX": ushort_max, @@ -313,7 +312,7 @@ def init_QtGui(): "GL_COLOR_BUFFER_BIT": GL_COLOR_BUFFER_BIT, "GL_NEAREST": GL_NEAREST, "WId": WId, - "PySide2.QtGui.QPlatformSurface": Virtual("PySide2.QtGui.QPlatformSurface"), # hmm... + "PySide2.QtGui.QPlatformSurface": int, # a handle "QList< QTouchEvent.TouchPoint >()": [], # XXX improve? "QPixmap()": Default("PySide2.QtGui.QPixmap"), # can't create without qApp "PySide2.QtCore.uint8_t": int, # macOS 5.9 @@ -325,6 +324,7 @@ def init_QtGui(): "zero(PySide2.QtGui.QTextLayout.FormatRange)": None, "zero(PySide2.QtGui.QTouchDevice)": None, "zero(PySide2.QtGui.QScreen)": None, + "PySide2.QtGui.QGenericMatrix": Missing("PySide2.QtGui.QGenericMatrix"), }) return locals() @@ -396,7 +396,6 @@ def init_QtMultimedia(): import PySide2.QtMultimedia import PySide2.QtMultimediaWidgets type_map.update({ - "QVariantMap": dict, "QGraphicsVideoItem": PySide2.QtMultimediaWidgets.QGraphicsVideoItem, "QVideoWidget": PySide2.QtMultimediaWidgets.QVideoWidget, }) diff --git a/sources/pyside2/PySide2/support/signature/parser.py b/sources/pyside2/PySide2/support/signature/parser.py index 9313fb540..dd6640fde 100644 --- a/sources/pyside2/PySide2/support/signature/parser.py +++ b/sources/pyside2/PySide2/support/signature/parser.py @@ -1,6 +1,6 @@ ############################################################################# ## -## Copyright (C) 2017 The Qt Company Ltd. +## Copyright (C) 2018 The Qt Company Ltd. ## Contact: https://www.qt.io/licensing/ ## ## This file is part of Qt for Python. @@ -48,6 +48,7 @@ import functools from .mapping import type_map, update_mapping, __dict__ as namespace _DEBUG = False +LIST_KEYWORDS = False """ parser.py @@ -119,6 +120,8 @@ def _parse_line(line): for arg in arglist: name, ann = arg.split(":") if name in keyword.kwlist: + if LIST_KEYWORDS: + print("KEYWORD", ret) name = name + "_" if "=" in ann: ann, default = ann.split("=") @@ -130,6 +133,10 @@ def _parse_line(line): multi = ret["multi"] if multi is not None: ret["multi"] = int(multi) + funcname = ret["funcname"] + parts = funcname.split(".") + if parts[-1] in keyword.kwlist: + ret["funcname"] = funcname + "_" return ret def make_good_value(thing, valtype): @@ -192,8 +199,14 @@ def calculate_props(line): arglist = res["arglist"] annotations = {} _defaults = [] - for tup in arglist: + for idx, tup in enumerate(arglist): name, ann = tup[:2] + if ann == "...": + name = "*args" + # copy the fields back :() + ann = 'NULL' # maps to None + tup = name, ann + arglist[idx] = tup annotations[name] = _resolve_type(ann, line) if len(tup) == 3: default = _resolve_value(tup[2], ann, line) @@ -214,6 +227,31 @@ def calculate_props(line): props["multi"] = res["multi"] return props +def fixup_multilines(sig_str): + lines = list(line.strip() for line in sig_str.strip().splitlines()) + res = [] + multi_lines = [] + for line in lines: + multi = re.match(r"([0-9]+):", line) + if multi: + idx, rest = int(multi.group(1)), line[multi.end():] + multi_lines.append(rest) + if idx > 0: + continue + # remove duplicates + multi_lines = list(set(multi_lines)) + # renumber or return a single line + nmulti = len(multi_lines) + if nmulti > 1: + for idx, line in enumerate(multi_lines): + res.append("{}:{}".format(nmulti-idx-1, line)) + else: + res.append(multi_lines[0]) + multi_lines = [] + else: + res.append(line) + return res + def pyside_type_init(typemod, sig_str): dprint() if type(typemod) is types.ModuleType: @@ -222,9 +260,10 @@ def pyside_type_init(typemod, sig_str): dprint("Initialization of type '{}.{}'".format(typemod.__module__, typemod.__name__)) update_mapping() + lines = fixup_multilines(sig_str) ret = {} multi_props = [] - for line in sig_str.strip().splitlines(): + for line in lines: props = calculate_props(line) shortname = props["name"] multi = props["multi"] @@ -232,10 +271,10 @@ def pyside_type_init(typemod, sig_str): ret[shortname] = props dprint(props) else: - fullname = props.pop("fullname") multi_props.append(props) if multi > 0: continue + fullname = props.pop("fullname") multi_props = {"multi": multi_props, "fullname": fullname} ret[shortname] = multi_props dprint(multi_props) diff --git a/sources/pyside2/PySide2/typesystem_templates.xml b/sources/pyside2/PySide2/typesystem_templates.xml index a17337258..a7a7bfc9d 100644 --- a/sources/pyside2/PySide2/typesystem_templates.xml +++ b/sources/pyside2/PySide2/typesystem_templates.xml @@ -361,6 +361,26 @@ Py_INCREF(%PYARG_0); </template> + <!-- Helpers for modifying "bool nativeEventFilter(QByteArray, void*, long *result)" + to return a tuple of bool,long --> + <template name="return_native_eventfilter_conversion_variables"> + long resultVar{0}; + long *%out = &resultVar; + </template> + <template name="return_native_eventfilter_conversion"> + %RETURN_TYPE %out = false; + if (PySequence_Check(%PYARG_0) && (PySequence_Size(%PYARG_0) == 2)) { + Shiboken::AutoDecRef pyItem(PySequence_GetItem(%PYARG_0, 0)); + %out = %CONVERTTOCPP[bool](pyItem); + } + </template> + + <template name="return_native_eventfilter"> + %PYARG_0 = PyTuple_New(2); + PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](%0)); + PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[long](*result_out)); + </template> + <!-- templates for __reduce__ --> <template name="reduce_code"> %PYARG_0 = Py_BuildValue("(N(%REDUCE_FORMAT))", PyObject_Type(%PYSELF), %REDUCE_ARGS); @@ -401,16 +421,6 @@ return pyData; </template> - <template name="matrix_fill_function"> - float value = %CONVERTTOCPP[float](%PYARG_1); - %CPPSELF.fill(value); - </template> - - <template name="matrix_transposed_function"> - %TRANSPOSED_TYPE transp = %CPPSELF.transposed(); - return %CONVERTTOPYTHON[%TRANSPOSED_TYPE](transp); - </template> - <!-- Replace '#' for the argument number you want. --> <template name="return_argument"> Py_INCREF(%PYARG_#); diff --git a/sources/pyside2/cmake/Macros/PySideModules.cmake b/sources/pyside2/cmake/Macros/PySideModules.cmake index 36488912d..0f8b500ac 100644 --- a/sources/pyside2/cmake/Macros/PySideModules.cmake +++ b/sources/pyside2/cmake/Macros/PySideModules.cmake @@ -80,7 +80,8 @@ macro(create_pyside_module get_filename_component(pyside_binary_dir ${CMAKE_CURRENT_BINARY_DIR} DIRECTORY) - add_custom_command(OUTPUT ${${module_sources}} + add_custom_command( OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/mjb_rejected_classes.log" + BYPRODUCTS ${${module_sources}} COMMAND "${SHIBOKEN_BINARY}" ${GENERATOR_EXTRA_FLAGS} "${pyside2_BINARY_DIR}/${module_name}_global.h" --include-paths=${shiboken_include_dirs} @@ -112,6 +113,7 @@ macro(create_pyside_module if(${module_deps}) add_dependencies(${module_name} ${${module_deps}}) endif() + create_generator_target(${module_name}) # install install(TARGETS ${module_name} LIBRARY DESTINATION ${PYTHON_SITE_PACKAGES}/PySide2) @@ -134,77 +136,6 @@ macro(create_pyside_module endforeach() endmacro() -#macro(check_qt_class_with_namespace module namespace class optional_source_files dropped_entries [namespace] [module]) -macro(check_qt_class module class optional_source_files dropped_entries) - if (${ARGC} GREATER 4) - set (namespace ${ARGV4}) - string(TOLOWER ${namespace} _namespace) - else () - set (namespace "") - endif () - if (${ARGC} GREATER 5) - set (include_file ${ARGV5}) - else () - set (include_file ${class}) - endif () - string(TOLOWER ${class} _class) - # Remove the "Qt" prefix. - string(SUBSTRING ${module} 2 -1 _module_no_qt_prefix) - if (_namespace) - set(_cppfile ${CMAKE_CURRENT_BINARY_DIR}/PySide2/${module}/${_namespace}_${_class}_wrapper.cpp) - else () - set(_cppfile ${CMAKE_CURRENT_BINARY_DIR}/PySide2/${module}/${_class}_wrapper.cpp) - endif () - if (DEFINED PYSIDE_${class}) - if (PYSIDE_${class}) - list(APPEND ${optional_source_files} ${_cppfile}) - else() - list(APPEND ${dropped_entries} PySide2.${module}.${class}) - endif() - else() - if (NOT ${namespace} STREQUAL "" ) - set (NAMESPACE_USE "using namespace ${namespace};") - else () - set (NAMESPACE_USE "") - endif () - set(SRC_FILE ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/test${class}.cxx) - file(WRITE ${SRC_FILE} - "#include <${include_file}>\n" - "${NAMESPACE_USE}\n" - "int main() { sizeof(${class}); }\n" - ) - - # Because Qt is built with -fPIC (by default), the compile tests also have to have that. - get_property(ADDITIONAL_FLAGS TARGET Qt5::Core PROPERTY INTERFACE_COMPILE_OPTIONS) - - # Don't add version tagging, because for some reason linker fails with: - # (.qtversion[qt_version_tag]+0x0): undefined reference to `qt_version_tag' - # Force usage of the C++11 standard. CMAKE_CXX_STANDARD does not work with try_compile - # but the issue has a fix in CMake 3.9. Thus we use a terrible workaround, we pass the C++ - # standard flag the way CheckCXXSourceCompiles.cmake does it. - - set(ADDITIONAL_FLAGS "${ADDITIONAL_FLAGS} -DQT_NO_VERSION_TAGGING ${CMAKE_CXX11_EXTENSION_COMPILE_OPTION}") - - try_compile(Q_WORKS ${CMAKE_BINARY_DIR} - ${SRC_FILE} - CMAKE_FLAGS - "-DINCLUDE_DIRECTORIES=${QT_INCLUDE_DIR};${Qt5${_module_no_qt_prefix}_INCLUDE_DIRS}" - "-DCOMPILE_DEFINITIONS:STRING=${ADDITIONAL_FLAGS}" - OUTPUT_VARIABLE OUTPUT) - file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeCheckQtClassTest.log ${OUTPUT}) - - set("PYSIDE_${class}" ${Q_WORKS} CACHE STRING "Has ${class} class been found?") - if(Q_WORKS) - message(STATUS "Checking for ${class} in ${module} -- found") - list(APPEND ${optional_source_files} ${_cppfile}) - else() - message(STATUS "Checking for ${class} in ${module} -- not found") - list(APPEND ${dropped_entries} PySide2.${module}.${class}) - endif() - endif() -endmacro() - - # Only add subdirectory if the associated Qt module is found. # As a side effect, this macro now also defines the variable ${name}_GEN_DIR # and must be called for every subproject. diff --git a/sources/pyside2/doc/codesnippets/doc/src/snippets/code/doc_src_qtcore.cpp b/sources/pyside2/doc/codesnippets/doc/src/snippets/code/doc_src_qtcore.py index 4696bd38b..4696bd38b 100644 --- a/sources/pyside2/doc/codesnippets/doc/src/snippets/code/doc_src_qtcore.cpp +++ b/sources/pyside2/doc/codesnippets/doc/src/snippets/code/doc_src_qtcore.py diff --git a/sources/pyside2/doc/codesnippets/doc/src/snippets/code/doc_src_qtnetwork.cpp b/sources/pyside2/doc/codesnippets/doc/src/snippets/code/doc_src_qtnetwork.py index 84e7e9189..84e7e9189 100644 --- a/sources/pyside2/doc/codesnippets/doc/src/snippets/code/doc_src_qtnetwork.cpp +++ b/sources/pyside2/doc/codesnippets/doc/src/snippets/code/doc_src_qtnetwork.py diff --git a/sources/pyside2/doc/codesnippets/doc/src/snippets/code/doc_src_qtopengl.cpp b/sources/pyside2/doc/codesnippets/doc/src/snippets/code/doc_src_qtopengl.py index 63c5665cd..63c5665cd 100644 --- a/sources/pyside2/doc/codesnippets/doc/src/snippets/code/doc_src_qtopengl.cpp +++ b/sources/pyside2/doc/codesnippets/doc/src/snippets/code/doc_src_qtopengl.py diff --git a/sources/pyside2/doc/codesnippets/doc/src/snippets/code/doc_src_qtprintsupport.cpp b/sources/pyside2/doc/codesnippets/doc/src/snippets/code/doc_src_qtprintsupport.py index fb5541603..fb5541603 100644 --- a/sources/pyside2/doc/codesnippets/doc/src/snippets/code/doc_src_qtprintsupport.cpp +++ b/sources/pyside2/doc/codesnippets/doc/src/snippets/code/doc_src_qtprintsupport.py diff --git a/sources/pyside2/doc/codesnippets/doc/src/snippets/code/doc_src_qtqml.cpp b/sources/pyside2/doc/codesnippets/doc/src/snippets/code/doc_src_qtqml.py index 3eeb024db..3eeb024db 100644 --- a/sources/pyside2/doc/codesnippets/doc/src/snippets/code/doc_src_qtqml.cpp +++ b/sources/pyside2/doc/codesnippets/doc/src/snippets/code/doc_src_qtqml.py diff --git a/sources/pyside2/doc/codesnippets/doc/src/snippets/code/doc_src_qtquick.cpp b/sources/pyside2/doc/codesnippets/doc/src/snippets/code/doc_src_qtquick.py index bf55f0c7e..bf55f0c7e 100644 --- a/sources/pyside2/doc/codesnippets/doc/src/snippets/code/doc_src_qtquick.cpp +++ b/sources/pyside2/doc/codesnippets/doc/src/snippets/code/doc_src_qtquick.py diff --git a/sources/pyside2/doc/codesnippets/doc/src/snippets/code/doc_src_qtsql.cpp b/sources/pyside2/doc/codesnippets/doc/src/snippets/code/doc_src_qtsql.py index 31849e785..31849e785 100644 --- a/sources/pyside2/doc/codesnippets/doc/src/snippets/code/doc_src_qtsql.cpp +++ b/sources/pyside2/doc/codesnippets/doc/src/snippets/code/doc_src_qtsql.py diff --git a/sources/pyside2/doc/codesnippets/doc/src/snippets/code/doc_src_qttest.cpp b/sources/pyside2/doc/codesnippets/doc/src/snippets/code/doc_src_qttest.py index 34dd7bb5a..34dd7bb5a 100644 --- a/sources/pyside2/doc/codesnippets/doc/src/snippets/code/doc_src_qttest.cpp +++ b/sources/pyside2/doc/codesnippets/doc/src/snippets/code/doc_src_qttest.py diff --git a/sources/pyside2/doc/codesnippets/doc/src/snippets/code/doc_src_qtwidgets.cpp b/sources/pyside2/doc/codesnippets/doc/src/snippets/code/doc_src_qtwidgets.py index a0deee957..a0deee957 100644 --- a/sources/pyside2/doc/codesnippets/doc/src/snippets/code/doc_src_qtwidgets.cpp +++ b/sources/pyside2/doc/codesnippets/doc/src/snippets/code/doc_src_qtwidgets.py diff --git a/sources/pyside2/doc/codesnippets/doc/src/snippets/code/doc_src_qtxml.cpp b/sources/pyside2/doc/codesnippets/doc/src/snippets/code/doc_src_qtxml.py index 077be436d..077be436d 100644 --- a/sources/pyside2/doc/codesnippets/doc/src/snippets/code/doc_src_qtxml.cpp +++ b/sources/pyside2/doc/codesnippets/doc/src/snippets/code/doc_src_qtxml.py diff --git a/sources/pyside2/doc/codesnippets/doc/src/snippets/doc_src_qtcharts.cpp b/sources/pyside2/doc/codesnippets/doc/src/snippets/doc_src_qtcharts.py index bfc35e1ee..bfc35e1ee 100644 --- a/sources/pyside2/doc/codesnippets/doc/src/snippets/doc_src_qtcharts.cpp +++ b/sources/pyside2/doc/codesnippets/doc/src/snippets/doc_src_qtcharts.py diff --git a/sources/pyside2/doc/codesnippets/doc/src/snippets/doc_src_qtgui.cpp b/sources/pyside2/doc/codesnippets/doc/src/snippets/doc_src_qtgui.py index 11c3cf5e9..11c3cf5e9 100644 --- a/sources/pyside2/doc/codesnippets/doc/src/snippets/doc_src_qtgui.cpp +++ b/sources/pyside2/doc/codesnippets/doc/src/snippets/doc_src_qtgui.py diff --git a/sources/pyside2/doc/codesnippets/doc/src/snippets/doc_src_qtmultimedia.cpp b/sources/pyside2/doc/codesnippets/doc/src/snippets/doc_src_qtmultimedia.py index 494145357..494145357 100644 --- a/sources/pyside2/doc/codesnippets/doc/src/snippets/doc_src_qtmultimedia.cpp +++ b/sources/pyside2/doc/codesnippets/doc/src/snippets/doc_src_qtmultimedia.py diff --git a/sources/pyside2/doc/codesnippets/doc/src/snippets/doc_src_qtxmlpatterns.cpp b/sources/pyside2/doc/codesnippets/doc/src/snippets/doc_src_qtxmlpatterns.py index c3363e97e..c3363e97e 100644 --- a/sources/pyside2/doc/codesnippets/doc/src/snippets/doc_src_qtxmlpatterns.cpp +++ b/sources/pyside2/doc/codesnippets/doc/src/snippets/doc_src_qtxmlpatterns.py diff --git a/sources/pyside2/doc/codesnippets/doc/src/snippets/quiloader/doc_src_qtuiloader.cpp b/sources/pyside2/doc/codesnippets/doc/src/snippets/quiloader/doc_src_qtuiloader.py index 141189ad8..141189ad8 100644 --- a/sources/pyside2/doc/codesnippets/doc/src/snippets/quiloader/doc_src_qtuiloader.cpp +++ b/sources/pyside2/doc/codesnippets/doc/src/snippets/quiloader/doc_src_qtuiloader.py diff --git a/sources/pyside2/doc/gettingstarted.rst b/sources/pyside2/doc/gettingstarted.rst index f24051c18..0a58226a7 100644 --- a/sources/pyside2/doc/gettingstarted.rst +++ b/sources/pyside2/doc/gettingstarted.rst @@ -4,15 +4,19 @@ Getting Started To get started with |project|, install the following prerequisites: -* Python v3.5 or later -* libclang v3.9 or later -* Optional: a virtual environment, such as `venv <https://docs.python.org/3/library/venv.html>`_ or `virtualenv <https://virtualenv.pypa.io/en/stable/installation>`_ +* Python 3.5+ or 2.7 +* libclang 5.0+ (for Qt 5.11) or 6.0+ (for Qt 5.12) +* Recommended: a virtual environment, such as `venv <https://docs.python.org/3/library/venv.html>`_ or `virtualenv <https://virtualenv.pypa.io/en/stable/installation>`_ With these installed, you are ready to install the |project| packages using the pip wheel. Run the following command from your command prompt to install:: - python -m pip install --index-url=http://download.qt.io/snapshots/ci/pyside/5.11/latest pyside2 --trusted-host download.qt.io + pip install PySide2 # For the latest version on PyPi + +or:: + + pip install --index-url=http://download.qt.io/snapshots/ci/pyside/5.12/latest pyside2 --trusted-host download.qt.io Now that you have |project| installed, you can test your setup by running the following Python constructs to print version information: @@ -44,16 +48,12 @@ guide you through the development process: def __init__(self): super().__init__() - self.hello = ["Hallo Welt", "ä½ å¥½ï¼Œä¸–ç•Œ", "Hei maailma",\ - "Hola Mundo", "Привет мир"] + self.hello = ["Hallo Welt", "Hei maailma", "Hola Mundo", "Привет мир"] self.button = QtWidgets.QPushButton("Click me!") self.text = QtWidgets.QLabel("Hello World") self.text.setAlignment(QtCore.Qt.AlignCenter) - self.text.setFont(QtGui.QFont("Titillium", 30)) - self.button.setFont(QtGui.QFont("Titillium", 20)) - self.layout = QtWidgets.QVBoxLayout() self.layout.addWidget(self.text) self.layout.addWidget(self.button) diff --git a/sources/pyside2/doc/pysideapi2.rst b/sources/pyside2/doc/pysideapi2.rst index f1cc13391..e552bf21d 100644 --- a/sources/pyside2/doc/pysideapi2.rst +++ b/sources/pyside2/doc/pysideapi2.rst @@ -1,12 +1,15 @@ .. _pysideapi2: -|pymodname| API -*************** +Qt for Python API +******************* One of the goals of |pymodname| is to be API compatible with PyQt5, with certain exceptions. For example, |pymodname| will not export C++ components that are marked as deprecated by Qt. +The latest considerations and known issues will be also reported +in the `wiki <https://wiki.qt.io/Qt_for_Python/Considerations>`_. + __hash__() function return value ================================ diff --git a/sources/pyside2/doc/pysideversion.rst b/sources/pyside2/doc/pysideversion.rst index 24afba12d..bde48b39e 100644 --- a/sources/pyside2/doc/pysideversion.rst +++ b/sources/pyside2/doc/pysideversion.rst @@ -8,20 +8,20 @@ numbers using the following python constructs: import PySide2.QtCore - # Prints PySide version - # e.g. 1.0.2 + # Prints PySide2 version + # e.g. 5.11.1a1 print(PySide2.__version__) # Gets a tuple with each version component - # e.g. (1, 0, 2, 'final', 1) + # e.g. (5, 11, 1, 'a', 1) print(PySide2.__version_info__) - # Prints the Qt version used to compile PySide - # e.g. "5.11.0" + # Prints the Qt version used to compile PySide2 + # e.g. "5.11.2" print(PySide2.QtCore.__version__) - # Gets a tuple with each version components of Qt used to compile PySide - # e.g. (5, 11, 0) + # Gets a tuple with each version components of Qt used to compile PySide2 + # e.g. (5, 11, 2) print(PySide2.QtCore.__version_info__) diff --git a/sources/pyside2/doc/tutorials/index.rst b/sources/pyside2/doc/tutorials/index.rst index 18bac57fd..c09b48ab5 100644 --- a/sources/pyside2/doc/tutorials/index.rst +++ b/sources/pyside2/doc/tutorials/index.rst @@ -1,5 +1,5 @@ -PySide examples and tutorials -***************************** +Qt for Python examples and tutorials +************************************* A collection of examples and tutorials with "walkthrough" guides are provided with |project| to help new users get started. These diff --git a/sources/pyside2/libpyside/CMakeLists.txt b/sources/pyside2/libpyside/CMakeLists.txt index 3069b1ca2..ec6713b62 100644 --- a/sources/pyside2/libpyside/CMakeLists.txt +++ b/sources/pyside2/libpyside/CMakeLists.txt @@ -17,6 +17,7 @@ if(${Qt5Quick_FOUND}) endif() endif() +set(QML_PRIVATE_API_SUPPORT 0) if(Qt5Qml_FOUND) # Used for registering custom QQuickItem classes defined in Python code. set(QML_SUPPORT 1) @@ -28,7 +29,6 @@ if(Qt5Qml_FOUND) set(QML_PRIVATE_API_SUPPORT 1) set(QML_INCLUDES ${QML_INCLUDES} ${Qt5Qml_PRIVATE_INCLUDE_DIRS}) else() - set(QML_PRIVATE_API_SUPPORT 0) message(WARNING "QML private API include files could not be found, support for catching QML exceptions inside Python code will not work.") endif() else() @@ -40,14 +40,10 @@ endif() qt5_wrap_cpp(DESTROYLISTENER_MOC "destroylistener.h") -configure_file("${CMAKE_CURRENT_SOURCE_DIR}/signalmanager.cpp.in" - "${CMAKE_CURRENT_BINARY_DIR}/signalmanager.cpp" @ONLY) - set(libpyside_SRC dynamicqmetaobject.cpp destroylistener.cpp - ${CMAKE_CURRENT_BINARY_DIR}/signalmanager.cpp - globalreceiver.cpp + signalmanager.cpp globalreceiverv2.cpp pysideclassinfo.cpp pysidemetafunction.cpp @@ -86,7 +82,8 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${SHIBOKEN_INCLUDE_DIR} ${SHIBOKEN_PYTHON_INCLUDE_DIR} ${QML_INCLUDES} - ${Qt5Core_INCLUDE_DIRS}) + ${Qt5Core_INCLUDE_DIRS} + ${Qt5Core_PRIVATE_INCLUDE_DIRS}) add_library(pyside2 SHARED ${libpyside_SRC} ${other_files}) target_link_libraries(pyside2 ${SHIBOKEN_PYTHON_LIBRARIES} @@ -107,6 +104,7 @@ endif() if(QML_SUPPORT) target_compile_definitions(pyside2 PUBLIC PYSIDE_QML_SUPPORT=1) endif() +target_compile_definitions(pyside2 PRIVATE PYSIDE_QML_PRIVATE_API_SUPPORT=${QML_PRIVATE_API_SUPPORT}) if(PYSIDE_QT_CONF_PREFIX) set_property(SOURCE pyside.cpp @@ -122,7 +120,6 @@ endif() set(libpyside_HEADERS destroylistener.h dynamicqmetaobject.h - globalreceiver.h pysideclassinfo.h pysidemacros.h signalmanager.h diff --git a/sources/pyside2/libpyside/destroylistener.cpp b/sources/pyside2/libpyside/destroylistener.cpp index 95e53f709..c6dc54713 100644 --- a/sources/pyside2/libpyside/destroylistener.cpp +++ b/sources/pyside2/libpyside/destroylistener.cpp @@ -40,10 +40,7 @@ #include <sbkpython.h> #include "destroylistener.h" -#include <QObject> #include <shiboken.h> -#include <QDebug> -#include <QMutex> PySide::DestroyListener* PySide::DestroyListener::m_instance = 0; diff --git a/sources/pyside2/libpyside/destroylistener.h b/sources/pyside2/libpyside/destroylistener.h index 0a800451a..b1a0597c5 100644 --- a/sources/pyside2/libpyside/destroylistener.h +++ b/sources/pyside2/libpyside/destroylistener.h @@ -40,10 +40,10 @@ #ifndef PYSIDE_DESTROY_LISTENER #define PYSIDE_DESTROY_LISTENER - -#include <QObject> #include "pysidemacros.h" +#include <QtCore/QObject> + namespace PySide { struct DestroyListenerPrivate; @@ -63,7 +63,7 @@ class PYSIDE_API DestroyListener : public QObject static DestroyListener* m_instance; DestroyListenerPrivate* m_d; DestroyListener(QObject *parent); - ~DestroyListener(); + ~DestroyListener() override; }; }//namespace diff --git a/sources/pyside2/libpyside/dynamicqmetaobject.cpp b/sources/pyside2/libpyside/dynamicqmetaobject.cpp index af2f416c6..5cbfa70f9 100644 --- a/sources/pyside2/libpyside/dynamicqmetaobject.cpp +++ b/sources/pyside2/libpyside/dynamicqmetaobject.cpp @@ -45,546 +45,376 @@ #include "pysideproperty_p.h" #include "pysideslot_p.h" -#include <QByteArray> -#include <QString> -#include <QStringList> -#include <QList> -#include <QLinkedList> -#include <QObject> -#include <cstring> -#include <QDebug> -#include <QMetaMethod> #include <shiboken.h> +#include <QtCore/QByteArray> +#include <QtCore/QObject> +#include <QtCore/QStringList> +#include <QtCore/QTextStream> +#include <QtCore/QVector> +#include <private/qmetaobjectbuilder_p.h> -#define EMPTY_META_METHOD "0()" +#include <cstring> +#include <vector> using namespace PySide; -enum PropertyFlags { - Invalid = 0x00000000, - Readable = 0x00000001, - Writable = 0x00000002, - Resettable = 0x00000004, - EnumOrFlag = 0x00000008, - StdCppSet = 0x00000100, -// Override = 0x00000200, - Constant = 0x00000400, - Final = 0x00000800, - Designable = 0x00001000, - ResolveDesignable = 0x00002000, - Scriptable = 0x00004000, - ResolveScriptable = 0x00008000, - Stored = 0x00010000, - ResolveStored = 0x00020000, - Editable = 0x00040000, - ResolveEditable = 0x00080000, - User = 0x00100000, - ResolveUser = 0x00200000, - Notify = 0x00400000 -}; - -// these values are from moc source code, generator.cpp:66 -enum MethodFlags { - AccessPrivate = 0x00, - AccessProtected = 0x01, - AccessPublic = 0x02, - MethodMethod = 0x00, - MethodSignal = 0x04, - MethodSlot = 0x08, - MethodConstructor = 0x0c, - MethodCompatibility = 0x10, - MethodCloned = 0x20, - MethodScriptable = 0x40 -}; - -enum MetaDataFlags { - IsUnresolvedType = 0x80000000, - TypeNameIndexMask = 0x7FFFFFFF -}; - -class DynamicQMetaObject::DynamicQMetaObjectPrivate +// MetaObjectBuilder: Provides the QMetaObject's returned by +// QObject::metaObject() for PySide2 objects. There are several +// scenarios to consider: +// 1) A plain Qt class (say QTimer) is instantiated. In that case, +// return the base meta object until a modification is made by +// adding methods, properties or class info (cf qmetaobject_test.py). +// In that case, instantiate a QMetaObjectBuilder inheriting the +// base meta meta object, add the method and return the result +// of QMetaObjectBuilder::toMetaObject() (with dirty handling should +// further modifications be made). +// 2) A Python class inheriting a Qt class is instantiated. For this, +// instantiate a QMetaObjectBuilder and add the methods/properties +// found by inspecting the Python class. + +class MetaObjectBuilderPrivate { public: - QList<MethodData> m_methods; - QList<PropertyData> m_properties; - - QMap<QByteArray, QByteArray> m_info; - QByteArray m_className; - bool m_updated; // when the meta data is not update - int m_methodOffset; - int m_propertyOffset; - int m_dataSize; - int m_emptyMethod; - int m_nullIndex; - - DynamicQMetaObjectPrivate() - : m_updated(false), m_methodOffset(0), m_propertyOffset(0), - m_dataSize(0), m_emptyMethod(-1), m_nullIndex(0) {} - - int createMetaData(QMetaObject* metaObj, QLinkedList<QByteArray> &strings); - void updateMetaObject(QMetaObject* metaObj); - void writeMethodsData(const QList<MethodData>& methods, unsigned int** data, QLinkedList<QByteArray>& strings, int* prtIndex, int nullIndex, int flags); - void writeStringData(char *, QLinkedList<QByteArray> &strings); + using MetaObjects = std::vector<const QMetaObject *>; + + QMetaObjectBuilder *ensureBuilder(); + void parsePythonType(PyTypeObject *type); + int indexOfMethod(QMetaMethod::MethodType mtype, + const QByteArray &signature) const; + int indexOfProperty(const QByteArray &name) const; + int addSlot(const QByteArray &signature); + int addSlot(const QByteArray &signature, const QByteArray &type); + int addSignal(const QByteArray &signature); + void removeMethod(QMetaMethod::MethodType mtype, int index); int getPropertyNotifyId(PySideProperty *property) const; -}; + int addProperty(const QByteArray &property, PyObject *data); + void addInfo(const QByteArray &key, const QByteArray &value); + void addInfo(const QMap<QByteArray, QByteArray> &info); + void removeProperty(int index); + const QMetaObject *update(); -bool sortMethodSignalSlot(const MethodData &m1, const MethodData &m2) -{ - if (m1.methodType() == QMetaMethod::Signal) - return m2.methodType() == QMetaMethod::Slot; - return false; -} + QMetaObjectBuilder *m_builder = nullptr; + + const QMetaObject *m_baseObject = nullptr; + MetaObjects m_cachedMetaObjects; + bool m_dirty = true; +}; -static int registerString(const QByteArray& s, QLinkedList<QByteArray>& strings) +QMetaObjectBuilder *MetaObjectBuilderPrivate::ensureBuilder() { - int idx = 0; - QLinkedList<QByteArray>::const_iterator it = strings.begin(); - QLinkedList<QByteArray>::const_iterator itEnd = strings.end(); - while (it != itEnd) { - if (strcmp(*it, s) == 0) - return idx; - ++idx; - ++it; + if (!m_builder) { + m_builder = new QMetaObjectBuilder(); + m_builder->setClassName(m_baseObject->className()); + m_builder->setSuperClass(m_baseObject); } - strings.append(s); - return idx; + return m_builder; } -static int blobSize(QLinkedList<QByteArray> &strings) +MetaObjectBuilder::MetaObjectBuilder(const char *className, const QMetaObject *metaObject) : + m_d(new MetaObjectBuilderPrivate) { - int size = strings.size() * sizeof(QByteArrayData); - - QByteArray str; - QByteArray debug_str; - foreach (const QByteArray &field, strings) { - str.append(field); - str.append(char(0)); - - debug_str.append(field); - debug_str.append('|'); - } - //qDebug()<<debug_str; - size += str.size(); - return size; + m_d->m_baseObject = metaObject; + m_d->m_builder = new QMetaObjectBuilder(); + m_d->m_builder->setClassName(className); + m_d->m_builder->setSuperClass(metaObject); + m_d->m_builder->setClassName(className); } -static int aggregateParameterCount(const QList<MethodData> &methods) +MetaObjectBuilder::MetaObjectBuilder(PyTypeObject *type, const QMetaObject *metaObject) + : m_d(new MetaObjectBuilderPrivate) { - int sum = 0; - for (int i = 0; i < methods.size(); ++i) - sum += methods.at(i).parameterCount() * 2 + 1; // nb_param*2 (type and names) +1 for return type - return sum; + m_d->m_baseObject = metaObject; + const char *className = type->tp_name; + if (const char *lastDot = strrchr(type->tp_name, '.')) + className = lastDot + 1; + // Different names indicate a Python class inheriting a Qt class. + // Parse the type. + if (strcmp(className, metaObject->className()) != 0) { + m_d->m_builder = new QMetaObjectBuilder(); + m_d->m_builder->setClassName(className); + m_d->m_builder->setSuperClass(metaObject); + m_d->parsePythonType(type); + } } -static void writeString(char *out, int i, const QByteArray &str, - const int offsetOfStringdataMember, int &stringdataOffset) +MetaObjectBuilder::~MetaObjectBuilder() { - int size = str.size(); - qptrdiff offset = offsetOfStringdataMember + stringdataOffset - - i * sizeof(QByteArrayData); - const QByteArrayData data = - Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(size, offset); - - memcpy(out + i * sizeof(QByteArrayData), &data, sizeof(QByteArrayData)); - - memcpy(out + offsetOfStringdataMember + stringdataOffset, str.constData(), size); - out[offsetOfStringdataMember + stringdataOffset + size] = '\0'; - - stringdataOffset += size + 1; + qDeleteAll(m_d->m_cachedMetaObjects); + delete m_d->m_builder; + delete m_d; } -static int qvariant_nameToType(const char* name) +int MetaObjectBuilderPrivate::indexOfMethod(QMetaMethod::MethodType mtype, + const QByteArray &signature) const { - if (!name) - return 0; - - if (strcmp(name, "QVariant") == 0) - return 0xffffffff; - if (strcmp(name, "QCString") == 0) - return QMetaType::QByteArray; - if (strcmp(name, "Q_LLONG") == 0) - return QMetaType::LongLong; - if (strcmp(name, "Q_ULLONG") == 0) - return QMetaType::ULongLong; - if (strcmp(name, "QIconSet") == 0) - return QMetaType::QIcon; - - uint tp = QMetaType::type(name); - return tp < QMetaType::User ? tp : 0; + int result = -1; + if (m_builder) { + switch (mtype) { + case QMetaMethod::Signal: + result = m_builder->indexOfSignal(signature); + break; + case QMetaMethod::Slot: + result = m_builder->indexOfSlot(signature); + break; + case QMetaMethod::Constructor: + result = m_builder->indexOfConstructor(signature); + break; + case QMetaMethod::Method: + result = m_builder->indexOfMethod(signature); + break; + } + if (result >= 0) + return result + m_baseObject->methodCount(); + } + switch (mtype) { + case QMetaMethod::Signal: + result = m_baseObject->indexOfSignal(signature); + break; + case QMetaMethod::Slot: + result = m_baseObject->indexOfSlot(signature); + break; + case QMetaMethod::Constructor: + result = m_baseObject->indexOfConstructor(signature); + break; + case QMetaMethod::Method: + result = m_baseObject->indexOfMethod(signature); + break; + } + return result; } -/* - Returns true if the type is a QVariant types. -*/ -static bool isVariantType(const char* type) +int MetaObjectBuilder::indexOfMethod(QMetaMethod::MethodType mtype, + const QByteArray &signature) const { - return qvariant_nameToType(type) != 0; + return m_d->indexOfMethod(mtype, signature); } -/*! - Returns true if the type is qreal. -*/ -static bool isQRealType(const char *type) +int MetaObjectBuilderPrivate::indexOfProperty(const QByteArray &name) const { - return strcmp(type, "qreal") == 0; + if (m_builder) { + const int result = m_builder->indexOfProperty(name); + if (result >= 0) + return m_baseObject->propertyCount() + result; + } + return m_baseObject->indexOfProperty(name); } -uint PropertyData::flags() const +int MetaObjectBuilder::indexOfProperty(const QByteArray &name) const { - const QByteArray btype(type()); - const char* typeName = btype.data(); - uint flags = Invalid; - if (!isVariantType(typeName)) - flags |= EnumOrFlag; - else if (!isQRealType(typeName)) - flags |= qvariant_nameToType(typeName) << 24; - - if (PySide::Property::isReadable(m_data)) - flags |= Readable; - - if (PySide::Property::isWritable(m_data)) - flags |= Writable; - - if (PySide::Property::hasReset(m_data)) - flags |= Resettable; - - if (PySide::Property::isDesignable(m_data)) - flags |= Designable; - else - flags |= ResolveDesignable; - - if (PySide::Property::isScriptable(m_data)) - flags |= Scriptable; - else - flags |= ResolveScriptable; - - if (PySide::Property::isStored(m_data)) - flags |= Stored; - else - flags |= ResolveStored; - - //EDITABLE - flags |= ResolveEditable; - - if (PySide::Property::isUser(m_data)) - flags |= User; - else - flags |= ResolveUser; - - if (m_cachedNotifyId != -1) - flags |= Notify; - - if (PySide::Property::isConstant(m_data)) - flags |= Constant; - - if (PySide::Property::isFinal(m_data)) - flags |= Final; - - return flags; + return m_d->indexOfProperty(name); } -// const QByteArray with EMPTY_META_METHOD, used to save some memory -const QByteArray MethodData::m_emptySig(EMPTY_META_METHOD); - -MethodData::MethodData() - : m_signature(m_emptySig) +static bool checkMethodSignature(const QByteArray &signature) { + // Common mistake not to add parentheses to the signature. + const int openParen = signature.indexOf('('); + const int closingParen = signature.lastIndexOf(')'); + const bool ok = openParen != -1 && closingParen != -1 && openParen < closingParen; + if (!ok) { + const QByteArray message = + "MetaObjectBuilder::addMethod: Invalid method signature provided for \"" + + signature + '"'; + PyErr_WarnEx(PyExc_RuntimeWarning, message.constData(), 0); + } + return ok; } -MethodData::MethodData(QMetaMethod::MethodType mtype, const QByteArray& signature, const QByteArray& rtype) - : m_signature(QMetaObject::normalizedSignature(signature.constData())) - , m_rtype(QMetaObject::normalizedSignature(rtype.constData())) - , m_mtype(mtype) +int MetaObjectBuilderPrivate::addSlot(const QByteArray &signature) { + if (!checkMethodSignature(signature)) + return -1; + m_dirty = true; + return m_baseObject->methodCount() + + ensureBuilder()->addSlot(signature).index(); } -void MethodData::clear() +int MetaObjectBuilder::addSlot(const char *signature) { - m_signature = m_emptySig; - m_rtype.clear(); + return m_d->addSlot(signature); } -bool MethodData::isValid() const +int MetaObjectBuilderPrivate::addSlot(const QByteArray &signature, + const QByteArray &type) { - return m_signature != m_emptySig; -} - -QList<QByteArray> MethodData::parameterTypes() const -{ - const char *signature = m_signature.constData(); - QList<QByteArray> list; - while (*signature && *signature != '(') - ++signature; - while (*signature && *signature != ')' && *++signature != ')') { - const char *begin = signature; - int level = 0; - while (*signature && (level > 0 || *signature != ',') && *signature != ')') { - if (*signature == '<') - ++level; - else if (*signature == '>') - --level; - ++signature; - } - list += QByteArray(begin, signature - begin); - } - return list; + if (!checkMethodSignature(signature)) + return -1; + m_dirty = true; + QMetaMethodBuilder methodBuilder = ensureBuilder()->addSlot(signature); + methodBuilder.setReturnType(type); + return m_baseObject->methodCount() + methodBuilder.index(); } -int MethodData::parameterCount() const +int MetaObjectBuilder::addSlot(const char *signature, const char *type) { - return parameterTypes().size(); + return m_d->addSlot(signature, type); } -QByteArray MethodData::name() const +int MetaObjectBuilderPrivate::addSignal(const QByteArray &signature) { - return m_signature.left(qMax(m_signature.indexOf('('), 0)); + if (!checkMethodSignature(signature)) + return -1; + m_dirty = true; + return m_baseObject->methodCount() + + ensureBuilder()->addSignal(signature).index(); } -PropertyData::PropertyData() - : m_cachedNotifyId(0), m_data(0) +int MetaObjectBuilder::addSignal(const char *signature) { + return m_d->addSignal(signature); } -PropertyData::PropertyData(const char* name, int notifyId, PySideProperty* data) - : m_name(name), m_cachedNotifyId(notifyId), m_data(data) +void MetaObjectBuilderPrivate::removeMethod(QMetaMethod::MethodType mtype, + int index) { + index -= m_baseObject->methodCount(); + auto builder = ensureBuilder(); + Q_ASSERT(index >= 0 && index < builder->methodCount()); + switch (mtype) { + case QMetaMethod::Constructor: + builder->removeConstructor(index); + break; + default: + builder->removeMethod(index); + break; + } + m_dirty = true; } -QByteArray PropertyData::type() const +void MetaObjectBuilder::removeMethod(QMetaMethod::MethodType mtype, int index) { - return QByteArray(PySide::Property::getTypeName(m_data)); + m_d->removeMethod(mtype, index); } - -bool PropertyData::isValid() const +int MetaObjectBuilderPrivate::getPropertyNotifyId(PySideProperty *property) const { - return !m_name.isEmpty(); + int notifyId = -1; + if (property->d->notify) { + if (const char *signalNotify = PySide::Property::getNotifyName(property)) + notifyId = indexOfMethod(QMetaMethod::Signal, signalNotify); + } + return notifyId; } -int PropertyData::cachedNotifyId() const +int MetaObjectBuilderPrivate::addProperty(const QByteArray &propertyName, + PyObject *data) { - return m_cachedNotifyId; -} + int index = indexOfProperty(propertyName); + if (index != -1) + return index; -bool PropertyData::operator==(const PropertyData& other) const -{ - return m_data == other.m_data; + PySideProperty *property = reinterpret_cast<PySideProperty *>(data); + int propertyNotifyId = getPropertyNotifyId(property); + if (propertyNotifyId >= 0) + propertyNotifyId -= m_baseObject->methodCount(); + auto newProperty = + ensureBuilder()->addProperty(propertyName, property->d->typeName, + propertyNotifyId); + index = newProperty.index() + m_baseObject->propertyCount(); + m_dirty = true; + return index; } -bool PropertyData::operator==(const char* name) const +int MetaObjectBuilder::addProperty(const char *property, PyObject *data) { - return m_name == name; + return m_d->addProperty(property, data); } - -DynamicQMetaObject::DynamicQMetaObject(PyTypeObject* type, const QMetaObject* base) - : m_d(new DynamicQMetaObjectPrivate) +void MetaObjectBuilderPrivate::addInfo(const QByteArray &key, + const QByteArray &value) { - d.superdata = base; - d.stringdata = NULL; - d.data = NULL; - d.extradata = NULL; - d.relatedMetaObjects = NULL; - d.static_metacall = NULL; - - m_d->m_className = QByteArray(type->tp_name).split('.').last(); - m_d->m_methodOffset = base->methodCount() - 1; - m_d->m_propertyOffset = base->propertyCount() - 1; - parsePythonType(type); + ensureBuilder()->addClassInfo(key, value); + m_dirty = true; } -DynamicQMetaObject::DynamicQMetaObject(const char* className, const QMetaObject* metaObject) - : m_d(new DynamicQMetaObjectPrivate) +void MetaObjectBuilder::addInfo(const char *key, const char *value) { - d.superdata = metaObject; - d.stringdata = 0; - d.data = 0; - d.extradata = 0; - d.relatedMetaObjects = NULL; - d.static_metacall = NULL; - - m_d->m_className = className; - m_d->m_methodOffset = metaObject->methodCount() - 1; - m_d->m_propertyOffset = metaObject->propertyCount() - 1; + m_d->addInfo(key, value); } -DynamicQMetaObject::~DynamicQMetaObject() +void MetaObjectBuilderPrivate::addInfo(const QMap<QByteArray, QByteArray> &info) { - free(reinterpret_cast<char *>(const_cast<QByteArrayData *>(d.stringdata))); - free(const_cast<uint*>(d.data)); - delete m_d; + auto builder = ensureBuilder(); + for (auto i = info.constBegin(), end = info.constEnd(); i != end; ++i) + builder->addClassInfo(i.key(), i.value()); + m_dirty = true; } -int DynamicQMetaObject::addMethod(QMetaMethod::MethodType mtype, const char* signature, const char* type) +void MetaObjectBuilder::addInfo(const QMap<QByteArray, QByteArray> &info) { - int index = -1; - int counter = 0; - - QList<MethodData>::iterator it = m_d->m_methods.begin(); - for (; it != m_d->m_methods.end(); ++it) { - if ((it->signature() == signature) && (it->methodType() == mtype)) - return m_d->m_methodOffset + counter; - else if (!it->isValid()) { - index = counter; - } - counter++; - } - - // Common mistake not to add parentheses to the signature. - if ((strchr(signature, ')') == 0) || ((strchr(signature, '(') == 0))) { - const QString message = - QLatin1String("DynamicQMetaObject::addMethod: Invalid method signature " - "provided for ") + QLatin1String(signature); - const QByteArray messageLatin = message.toLatin1(); - PyErr_WarnEx(PyExc_RuntimeWarning, messageLatin.constData(), 0); - return -1; - } - - //has blank method - if (index != -1) { - m_d->m_methods[index] = MethodData(mtype, signature, type); - index++; - } else { - m_d->m_methods << MethodData(mtype, signature, type); - index = m_d->m_methods.size(); - } - - m_d->m_updated = false; - return m_d->m_methodOffset + index; + m_d->addInfo(info); } -void DynamicQMetaObject::removeMethod(QMetaMethod::MethodType mtype, uint index) +void MetaObjectBuilderPrivate::removeProperty(int index) { - const char* methodSig = method(index).methodSignature(); - QList<MethodData>::iterator it = m_d->m_methods.begin(); - for (; it != m_d->m_methods.end(); ++it) { - if ((it->signature() == methodSig) && (it->methodType() == mtype)){ - it->clear(); - m_d->m_updated = false; - break; - } - } -} - -int DynamicQMetaObject::addSignal(const char* signal, const char* type) -{ - return addMethod(QMetaMethod::Signal, signal, type); + index -= m_baseObject->propertyCount(); + auto builder = ensureBuilder(); + Q_ASSERT(index >= 0 && index < builder->propertyCount()); + builder->removeProperty(index); + m_dirty = true; } -int DynamicQMetaObject::addSlot(const char* slot, const char* type) +void MetaObjectBuilder::removeProperty(int index) { - return addMethod(QMetaMethod::Slot, slot, type); + m_d->removeProperty(index); } -void DynamicQMetaObject::removeSlot(uint index) -{ - removeMethod(QMetaMethod::Slot, index); -} - -void DynamicQMetaObject::removeSignal(uint index) -{ - removeMethod(QMetaMethod::Signal, index); -} +// PYSIDE-315: Instead of sorting the items and maybe breaking indices, we +// ensure that the signals and slots are sorted by the improved +// parsePythonType() (signals must go before slots). The order can only +// become distorted if the class is modified after creation. In that +// case, we give a warning. -int DynamicQMetaObject::addProperty(const char* propertyName, PyObject* data) +static QString msgMethodSortOrder(const QMetaObject *mo, int offendingIndex) { - int index = m_d->m_properties.indexOf(propertyName); - if (index != -1) - return m_d->m_propertyOffset + index; - - // retrieve notifyId - PySideProperty *property = reinterpret_cast<PySideProperty *>(data); - const int notifyId = m_d->getPropertyNotifyId(property); - - //search for a empty space - PropertyData blank; - index = m_d->m_properties.indexOf(blank); - if (index != -1) { - m_d->m_properties[index] = PropertyData(propertyName, notifyId, property); - } else { - m_d->m_properties << PropertyData(propertyName, notifyId, property); - index = m_d->m_properties.size(); + QString result; + QTextStream str(&result); + str << "\n\n*** Sort Warning ***\nSignals and slots in QMetaObject '" + << mo->className() + << "' are not ordered correctly, this may lead to issues.\n"; + const int methodOffset = mo->methodOffset(); + for (int m = methodOffset, methodCount = mo->methodCount(); m < methodCount; ++m) { + const auto method = mo->method(m); + str << (m - methodOffset + 1) << (m > offendingIndex ? '!' : ' ') + << (method.methodType() == QMetaMethod::Signal ? " Signal " : " Slot ") + << method.methodSignature() << '\n'; } - m_d->m_updated = false; - return m_d->m_propertyOffset + index; + return result; } -int DynamicQMetaObject::DynamicQMetaObjectPrivate::getPropertyNotifyId(PySideProperty *property) const +static void checkMethodOrder(const QMetaObject *metaObject) { - int notifyId = -1; - if (property->d->notify) { - const char *signalNotify = PySide::Property::getNotifyName(property); - if (signalNotify) { - const MethodData signalObject(QMetaMethod::Signal, signalNotify, ""); - notifyId = m_methods.indexOf(signalObject); + const int lastMethod = metaObject->methodCount() - 1; + for (int m = metaObject->methodOffset(); m < lastMethod; ++m) { + if (metaObject->method(m).methodType() == QMetaMethod::Slot + && metaObject->method(m + 1).methodType() == QMetaMethod::Signal) { + const auto message = msgMethodSortOrder(metaObject, m); + PyErr_WarnEx(PyExc_RuntimeWarning, qPrintable(message), 0); + // Prevent a warning from being turned into an error. We cannot easily unwind. + PyErr_Clear(); + break; } } - return notifyId; } -void DynamicQMetaObject::addInfo(const char* key, const char* value) +const QMetaObject *MetaObjectBuilderPrivate::update() { - m_d->m_info[key] = value; -} - -void DynamicQMetaObject::addInfo(QMap<QByteArray, QByteArray> info) -{ - QMap<QByteArray, QByteArray>::const_iterator i = info.constBegin(); - while (i != info.constEnd()) { - m_d->m_info[i.key()] = i.value(); - ++i; - } - m_d->m_updated = false; -} - -const QMetaObject* DynamicQMetaObject::update() const -{ - if (!m_d->m_updated) { - m_d->updateMetaObject(const_cast<DynamicQMetaObject*>(this)); - m_d->m_updated = true; + if (!m_builder) + return m_baseObject; + if (m_cachedMetaObjects.empty() || m_dirty) { + m_cachedMetaObjects.push_back(m_builder->toMetaObject()); + checkMethodOrder(m_cachedMetaObjects.back()); + m_dirty = false; } - return this; + return m_cachedMetaObjects.back(); } -void DynamicQMetaObject::DynamicQMetaObjectPrivate::writeMethodsData(const QList<MethodData>& methods, - unsigned int** data, - QLinkedList<QByteArray>& strings, - int* prtIndex, - int nullIndex, - int flags) +const QMetaObject *MetaObjectBuilder::update() { - int index = *prtIndex; - int paramsIndex = index + methods.count() * 5; - - QList<MethodData>::const_iterator it = methods.begin(); - - if (m_emptyMethod == -1) - m_emptyMethod = registerString(EMPTY_META_METHOD, strings); - - for (; it != methods.end(); ++it) { - int name_idx = 0; - int argc = it->parameterCount(); - if (it->signature() != EMPTY_META_METHOD) - name_idx = registerString(it->name(), strings); - else - name_idx = m_emptyMethod; // func name - - (*data)[index++] = name_idx; - (*data)[index++] = argc; // argc (previously: arg name) - (*data)[index++] = paramsIndex; //parameter index - (*data)[index++] = nullIndex; // tags - (*data)[index++] = flags | (it->methodType() == QMetaMethod::Signal ? MethodSignal : MethodSlot); - - if (it->methodType() == QMetaMethod::Signal) - (*data)[13] += 1; //signal count - - paramsIndex += 1 + argc * 2; - } - *prtIndex = index; + return m_d->update(); } -void DynamicQMetaObject::parsePythonType(PyTypeObject *type) +void MetaObjectBuilderPrivate::parsePythonType(PyTypeObject *type) { // Get all non-QObject-derived base types in method resolution order, filtering out the types // that can't have signals, slots or properties. @@ -601,9 +431,8 @@ void DynamicQMetaObject::parsePythonType(PyTypeObject *type) || baseType == reinterpret_cast<PyTypeObject *>(SbkObject_TypeF()) || baseType == reinterpret_cast<PyTypeObject *>(&PyBaseObject_Type)) { continue; - } else { - basesToCheck.append(baseType); } + basesToCheck.append(baseType); } // Prepend the actual type that we are parsing. @@ -630,8 +459,8 @@ void DynamicQMetaObject::parsePythonType(PyTypeObject *type) if (data->signatures[i]) sig += data->signatures[i]; sig += ')'; - if (d.superdata->indexOfSignal(sig) == -1) - addSignal(sig, "void"); + if (m_baseObject->indexOfSignal(sig) == -1) + m_builder->addSignal(sig); } } } @@ -655,7 +484,7 @@ void DynamicQMetaObject::parsePythonType(PyTypeObject *type) if (Property::checkType(value)) { // Leave the properties to be registered after signals because they may depend on // notify signals. - int index = d.superdata->indexOfProperty(Shiboken::String::toCString(key)); + int index = m_baseObject->indexOfProperty(Shiboken::String::toCString(key)); if (index == -1) properties << PropPair(Shiboken::String::toCString(key), value); } else if (PyFunction_Check(value)) { @@ -663,239 +492,30 @@ void DynamicQMetaObject::parsePythonType(PyTypeObject *type) if (PyObject_HasAttr(value, slotAttrName)) { PyObject *signatureList = PyObject_GetAttr(value, slotAttrName); for (Py_ssize_t i = 0, i_max = PyList_Size(signatureList); i < i_max; ++i) { - PyObject *signature = PyList_GET_ITEM(signatureList, i); - QByteArray sig(Shiboken::String::toCString(signature)); + PyObject *pySignature = PyList_GET_ITEM(signatureList, i); + QByteArray signature(Shiboken::String::toCString(pySignature)); // Split the slot type and its signature. - QList<QByteArray> slotInfo = sig.split(' '); - int index = d.superdata->indexOfSlot(slotInfo[1]); - if (index == -1) - addSlot(slotInfo[1], slotInfo[0]); + QByteArray type; + const int spacePos = signature.indexOf(' '); + if (spacePos != -1) { + type = signature.left(spacePos); + signature.remove(0, spacePos + 1); + } + int index = m_baseObject->indexOfSlot(signature); + if (index == -1) { + if (type.isEmpty() || type == "void") { + addSlot(signature); + } else { + addSlot(signature, type); + } + } } } } } // Register properties - foreach (const PropPair &propPair, properties) + for (const PropPair &propPair : qAsConst(properties)) addProperty(propPair.first, propPair.second); } } - -/*! - Allocate the meta data table. - Returns the index in the table corresponding to the header fields count. -*/ -int DynamicQMetaObject::DynamicQMetaObjectPrivate::createMetaData(QMetaObject* metaObj, QLinkedList<QByteArray> &strings) -{ - const int n_methods = m_methods.size(); - const int n_properties = m_properties.size(); - const int n_info = m_info.size(); - - int header[] = {7, // revision (Used by moc, qmetaobjectbuilder and qdbus) - 0, // class name index in m_metadata - n_info, 0, // classinfo and classinfo index - n_methods, 0, // method count and method list index - n_properties, 0, // prop count and prop indexes - 0, 0, // enum count and enum index - 0, 0, // constructors (since revision 2) - 0, // flags (since revision 3) - 0}; // signal count (since revision 4) - - const int HEADER_LENGHT = sizeof(header)/sizeof(int); - - m_dataSize = HEADER_LENGHT; - m_dataSize += n_info*2; //class info: name, value - m_dataSize += n_methods*5; //method: name, argc, parameters, tag, flags - m_dataSize += n_properties*4; //property: name, type, flags - m_dataSize += 1; //eod - - m_dataSize += aggregateParameterCount(m_methods); // types and parameter names - - uint* data = reinterpret_cast<uint*>(realloc(const_cast<uint*>(metaObj->d.data), m_dataSize * sizeof(uint))); - - Q_ASSERT(data); - std::memcpy(data, header, sizeof(header)); - - metaObj->d.data = data; - - return HEADER_LENGHT; -} - -// Writes strings to string data struct. -// The struct consists of an array of QByteArrayData, followed by a char array -// containing the actual strings. This format must match the one produced by -// moc (see generator.cpp). -void DynamicQMetaObject::DynamicQMetaObjectPrivate::writeStringData(char *out, QLinkedList<QByteArray> &strings) -{ - Q_ASSERT(!(reinterpret_cast<quintptr>(out) & (Q_ALIGNOF(QByteArrayData)-1))); - - int offsetOfStringdataMember = strings.size() * sizeof(QByteArrayData); - int stringdataOffset = 0; - int i = 0; - foreach(const QByteArray& str, strings) { - writeString(out, i, str, offsetOfStringdataMember, stringdataOffset); - i++; - } -} - -QList<MethodData>::iterator is_sorted_until(QList<MethodData>::iterator first, - QList<MethodData>::iterator last, - bool comp(const MethodData &m1, const MethodData &m2)) -{ - if (first != last) { - QList<MethodData>::iterator next = first; - while (++next != last) { - if (comp(*next, *first)) - return next; - ++first; - } - } - return last; -} - -bool is_sorted(QList<MethodData>::iterator first, QList<MethodData>::iterator last, - bool comp(const MethodData &m1, const MethodData &m2)) -{ - return is_sorted_until(first, last, comp) == last; -} - -void DynamicQMetaObject::DynamicQMetaObjectPrivate::updateMetaObject(QMetaObject* metaObj) -{ - Q_ASSERT(!m_updated); - uint *data = const_cast<uint*>(metaObj->d.data); - int index = 0; - QLinkedList<QByteArray> strings; - m_dataSize = 0; - - // Recompute the size and reallocate memory - // index is set after the last header field. - index = createMetaData(metaObj, strings); - data = const_cast<uint*>(metaObj->d.data); - - registerString(m_className, strings); // register class string - m_nullIndex = registerString("", strings); // register a null string - - // Write class info. - if (m_info.size()) { - if (data[3] == 0) - data[3] = index; - - QMap<QByteArray, QByteArray>::const_iterator i = m_info.constBegin(); //TODO: info is a hash this can fail - while (i != m_info.constEnd()) { - int valueIndex = registerString(i.value(), strings); - int keyIndex = registerString(i.key(), strings); - data[index++] = keyIndex; - data[index++] = valueIndex; - i++; - } - } - - // Write methods first, then properties, to be consistent with moc. - // Write signals/slots (signals must be written first, see indexOfMethodRelative in - // qmetaobject.cpp). - - QList<MethodData>::iterator it; - // PYSIDE-315: Instead of sorting the items and maybe breaking indices, - // we ensure that the signals and slots are sorted by the improved parsePythonType(). - // The order can only become distorted if the class is modified after creation. - // In that case, we give a warning. - if (!is_sorted(m_methods.begin(), m_methods.end(), sortMethodSignalSlot)) { - const char *metaObjectName = this->m_className.data(); - PyObject *txt = PyBytes_FromFormat("\n\n*** Sort Warning ***\n" - "Signals and slots in QMetaObject '%s' are not ordered correctly, " - "this may lead to issues.\n", metaObjectName); - it = m_methods.begin(); - QList<MethodData>::iterator end = m_methods.end(); - QList<MethodData>::iterator until = is_sorted_until(m_methods.begin(), m_methods.end(), - sortMethodSignalSlot); - for (; it != end; ++it) { - PyObject *atxt = PyBytes_FromFormat("%d%s %s %s\n", it - m_methods.begin() + 1, - until >= it + 1 ? " " : "!", - it->methodType() == QMetaMethod::Signal ? "Signal" : "Slot ", - it->signature().data() ); - PyBytes_ConcatAndDel(&txt, atxt); - } - PyErr_WarnEx(PyExc_RuntimeWarning, PyBytes_AsString(txt), 0); - Py_DECREF(txt); - // Prevent a warning from being turned into an error. We cannot easily unwind. - PyErr_Clear(); - } - - if (m_methods.size()) { - if (data[5] == 0) - data[5] = index; - - writeMethodsData(m_methods, &data, strings, &index, m_nullIndex, AccessPublic); - } - - // Write signal/slots parameters. - if (m_methods.size()) { - for (it = m_methods.begin(); it != m_methods.end(); ++it) { - QList<QByteArray> paramTypeNames = it->parameterTypes(); - int paramCount = paramTypeNames.size(); - for (int i = -1; i < paramCount; ++i) { - const QByteArray &typeName = (i < 0) ? it->returnType() : paramTypeNames.at(i); - int typeInfo; - if (QtPrivate::isBuiltinType(typeName)) - typeInfo = QMetaType::type(typeName); - else - typeInfo = IsUnresolvedType | registerString(typeName, strings); - data[index++] = typeInfo; - } - - // Parameter names (use a null string) - for (int i = 0; i < paramCount; ++i) { - data[index++] = m_nullIndex; - } - } - } - - // Write properties. - if (m_properties.size()) { - if (data[7] == 0) - data[7] = index; - - QList<PropertyData>::const_iterator i = m_properties.constBegin(); - while (i != m_properties.constEnd()) { - if (i->isValid()) { - data[index++] = registerString(i->name(), strings); // name - } else - data[index++] = m_nullIndex; - - // Find out the property type index. - int typeInfo = m_nullIndex; - if (i->isValid()) { - const QByteArray &typeName = i->type(); - if (QtPrivate::isBuiltinType(typeName)) - typeInfo = QMetaType::type(typeName); - else - typeInfo = IsUnresolvedType | registerString(typeName, strings); - } - data[index++] = typeInfo; // normalized type - - data[index++] = i->flags(); - i++; - } - - // Write properties notify. - i = m_properties.constBegin(); - while (i != m_properties.constEnd()) { - // Recompute notifyId, because sorting the methods might have changed the relative - // index. - const int notifyId = getPropertyNotifyId(i->data()); - data[index++] = notifyId >= 0 ? static_cast<uint>(notifyId) : 0; //signal notify index - i++; - } - } - - data[index++] = 0; // the end - - // Create the m_metadata string. - int size = blobSize(strings); - char *blob = - reinterpret_cast<char *>(realloc(reinterpret_cast<char *>(const_cast<QByteArrayData *>(metaObj->d.stringdata)), size)); - writeStringData(blob, strings); - - metaObj->d.stringdata = reinterpret_cast<const QByteArrayData *>(blob); - metaObj->d.data = data; -} diff --git a/sources/pyside2/libpyside/dynamicqmetaobject.h b/sources/pyside2/libpyside/dynamicqmetaobject.h index 5ecce50c9..1fbe73ea4 100644 --- a/sources/pyside2/libpyside/dynamicqmetaobject.h +++ b/sources/pyside2/libpyside/dynamicqmetaobject.h @@ -40,43 +40,42 @@ #ifndef DYNAMICQMETAOBJECT_H #define DYNAMICQMETAOBJECT_H -#include "pysidemacros.h" #include <sbkpython.h> -#include <QMetaObject> -#include <QMetaMethod> + +#include <QtCore/QMetaObject> +#include <QtCore/QMetaMethod> + +class MetaObjectBuilderPrivate; namespace PySide { -class DynamicQMetaObject : public QMetaObject +class MetaObjectBuilder { + Q_DISABLE_COPY(MetaObjectBuilder) public: - DynamicQMetaObject(const char* className, const QMetaObject* metaObject); - DynamicQMetaObject(PyTypeObject* type, const QMetaObject* metaobject); - ~DynamicQMetaObject(); + MetaObjectBuilder(const char *className, const QMetaObject *metaObject); + MetaObjectBuilder(PyTypeObject *type, const QMetaObject *metaObject); + ~MetaObjectBuilder(); - int addMethod(QMetaMethod::MethodType mtype, const char* signature, const char* type); - void removeMethod(QMetaMethod::MethodType mtype, uint index); - int addSignal(const char* signal, const char* type = 0); - int addSlot(const char* slot, const char* type = 0); - int addProperty(const char* property, PyObject* data); - void addInfo(const char* key, const char* value); - void addInfo(QMap<QByteArray, QByteArray> info); + int indexOfMethod(QMetaMethod::MethodType mtype, const QByteArray &signature) const; + int indexOfProperty(const QByteArray &name) const; + int addSlot(const char *signature); + int addSlot(const char *signature, const char *type); + int addSignal(const char *signature); + void removeMethod(QMetaMethod::MethodType mtype, int index); + int addProperty(const char *property, PyObject *data); + void addInfo(const char *key, const char *value); + void addInfo(const QMap<QByteArray, QByteArray> &info); - void removeSignal(uint idex); - void removeSlot(uint index); - void removeProperty(uint index); + void removeProperty(int index); - const QMetaObject* update() const; + const QMetaObject *update(); private: - class DynamicQMetaObjectPrivate; - DynamicQMetaObjectPrivate* m_d; - - void parsePythonType(PyTypeObject *type); + MetaObjectBuilderPrivate *m_d; }; - } #endif diff --git a/sources/pyside2/libpyside/dynamicqmetaobject_p.h b/sources/pyside2/libpyside/dynamicqmetaobject_p.h index 219ffc4e3..738b950ba 100644 --- a/sources/pyside2/libpyside/dynamicqmetaobject_p.h +++ b/sources/pyside2/libpyside/dynamicqmetaobject_p.h @@ -41,8 +41,9 @@ #define DYNAMICMETAPROPERTY_P_H #include <sbkpython.h> -#include <QByteArray> -#include <QMetaMethod> + +#include <QtCore/QByteArray> +#include <QtCore/QMetaMethod> #define GLOBAL_RECEIVER_CLASS_NAME "__GlobalReceiver__" diff --git a/sources/pyside2/libpyside/globalreceiver.cpp b/sources/pyside2/libpyside/globalreceiver.cpp deleted file mode 100644 index ee1f6354a..000000000 --- a/sources/pyside2/libpyside/globalreceiver.cpp +++ /dev/null @@ -1,329 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $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 "globalreceiver.h" -#include "dynamicqmetaobject_p.h" -#include "pysideweakref.h" - -#include <QMetaMethod> -#include <QDebug> -#include <QEvent> -#include <QLinkedList> -#include <autodecref.h> -#include <sbkconverter.h> -#include <gilstate.h> - -#include "signalmanager.h" - -#define RECEIVER_DESTROYED_SLOT_NAME "__receiverDestroyed__(QObject*)" - -namespace PySide -{ -class DynamicSlotData -{ - public: - DynamicSlotData(int id, PyObject* callback, GlobalReceiver* parent); - void addRef(const QObject* o); - void decRef(const QObject* o); - void clear(); - int hasRefTo(const QObject* o) const; - int refCount() const; - int id() const; - PyObject* call(PyObject* args); - ~DynamicSlotData(); - static void onCallbackDestroyed(void* data); - - private: - int m_id; - bool m_isMethod; - PyObject* m_callback; - PyObject* m_pythonSelf; - PyObject* m_pyClass; - PyObject* m_weakRef; - GlobalReceiver* m_parent; - QLinkedList<const QObject*> m_refs; -}; - -} - -using namespace PySide; - -DynamicSlotData::DynamicSlotData(int id, PyObject* callback, GlobalReceiver* parent) - : m_id(id), m_pythonSelf(0), m_pyClass(0), m_weakRef(0), m_parent(parent) -{ - Shiboken::GilState gil; - - m_isMethod = PyMethod_Check(callback); - if (m_isMethod) { - //Can not store calback pointe because this will be destroyed at the end of the scope - //To avoid increment intance reference keep the callback information - m_callback = PyMethod_GET_FUNCTION(callback); -#ifdef IS_PY3K - m_pyClass = 0; -#else - m_pyClass = PyMethod_GET_CLASS(callback); -#endif - - m_pythonSelf = PyMethod_GET_SELF(callback); - - //monitor class from method lifetime - m_weakRef = WeakRef::create(m_pythonSelf, DynamicSlotData::onCallbackDestroyed, this); - } else { - m_callback = callback; - Py_INCREF(m_callback); - } -} - -PyObject* DynamicSlotData::call(PyObject* args) -{ - PyObject* callback = m_callback; - - //create a callback based on method data - Shiboken::GilState gil; - if (m_isMethod) -#ifdef IS_PY3K - callback = PyMethod_New(callback, m_pythonSelf); -#else - callback = PyMethod_New(callback, m_pythonSelf, m_pyClass); -#endif - - PyObject* result = PyObject_CallObject(callback, args); - - if (m_isMethod) - Py_DECREF(callback); - - return result; -} - -void DynamicSlotData::addRef(const QObject *o) -{ - m_refs.append(o); -} - -void DynamicSlotData::decRef(const QObject *o) -{ - m_refs.removeOne(o); -} - -int DynamicSlotData::refCount() const -{ - return m_refs.size(); -} - -int DynamicSlotData::id() const -{ - return m_id; -} - -int DynamicSlotData::hasRefTo(const QObject *o) const -{ - return m_refs.count(o); -} - -void DynamicSlotData::clear() -{ - Shiboken::GilState gil; - Py_XDECREF(m_weakRef); - m_weakRef = 0; - m_refs.clear(); -} - -DynamicSlotData::~DynamicSlotData() -{ - Shiboken::GilState gil; - clear(); - if (!m_isMethod) - Py_DECREF(m_callback); -} - -void DynamicSlotData::onCallbackDestroyed(void *data) -{ - Shiboken::GilState gil; - DynamicSlotData* self = reinterpret_cast<DynamicSlotData*>(data); - - //Disconnect all sources - QMetaMethod m = self->m_parent->metaObject()->method(self->m_id); - QByteArray methodName = QByteArray::number(m.methodType()).append(m.methodSignature()); - QLinkedList<const QObject*> sources = self->m_refs; - foreach(const QObject* src, sources) - const_cast<QObject*>(src)->disconnect(self->m_parent, methodName); - self->m_weakRef = 0; -} - -GlobalReceiver::GlobalReceiver() - : m_metaObject(GLOBAL_RECEIVER_CLASS_NAME, &QObject::staticMetaObject) -{ - //slot used to be notifyed of object destrouction - m_metaObject.addSlot(RECEIVER_DESTROYED_SLOT_NAME); - m_metaObject.update(); - setObjectName(QLatin1String("GLOBAL RECEIVER")); -} - -GlobalReceiver::~GlobalReceiver() -{ - while(!m_slotReceivers.empty()) { - DynamicSlotData* data = m_slotReceivers.take(m_slotReceivers.begin().key()); - data->clear(); - delete data; - } -} - -void GlobalReceiver::connectNotify(QObject* source, int slotId) -{ - if (m_slotReceivers.contains(slotId)) { - DynamicSlotData* data = m_slotReceivers[slotId]; - if (!data->hasRefTo(source)) - QObject::connect(source, SIGNAL(destroyed(QObject*)), this, "1" RECEIVER_DESTROYED_SLOT_NAME); - data->addRef(source); - } -} - -void GlobalReceiver::disconnectNotify(QObject* source, int slotId) -{ - if (m_slotReceivers.contains(slotId)) { - DynamicSlotData *data = m_slotReceivers[slotId]; - data->decRef(source); - if (data->refCount() == 0) - removeSlot(slotId); - - if (!hasConnectionWith(source)) - QObject::disconnect(source, SIGNAL(destroyed(QObject*)), this, "1" RECEIVER_DESTROYED_SLOT_NAME); - } -} - -const QMetaObject* GlobalReceiver::metaObject() const -{ - return m_metaObject.update(); -} - -int GlobalReceiver::addSlot(const char* slot, PyObject* callback) -{ - int slotId = m_metaObject.addSlot(slot); - if (!m_slotReceivers.contains(slotId)) - m_slotReceivers[slotId] = new DynamicSlotData(slotId, callback, this); - - bool isShortCircuit = true; - for (int i = 0; slot[i]; ++i) { - if (slot[i] == '(') { - isShortCircuit = false; - break; - } - } - - if (isShortCircuit) - m_shortCircuitSlots << slotId; - - Q_ASSERT(slotId >= QObject::staticMetaObject.methodCount()); - return slotId; -} - -void GlobalReceiver::removeSlot(int slotId) -{ - if (m_slotReceivers.contains(slotId)) { - delete m_slotReceivers.take(slotId); - m_metaObject.removeSlot(slotId); - m_shortCircuitSlots.remove(slotId); - } -} - -bool GlobalReceiver::hasConnectionWith(const QObject *object) -{ - QHash<int, DynamicSlotData*>::iterator i = m_slotReceivers.begin(); - while(i != m_slotReceivers.end()) { - if (i.value()->hasRefTo(object)) { - return true; - } - i++; - } - return false; -} - -int GlobalReceiver::qt_metacall(QMetaObject::Call call, int id, void** args) -{ - Q_ASSERT(call == QMetaObject::InvokeMetaMethod); - Q_ASSERT(id >= QObject::staticMetaObject.methodCount()); - QMetaMethod slot = metaObject()->method(id); - Q_ASSERT(slot.methodType() == QMetaMethod::Slot); - - if (strcmp(slot.methodSignature(), RECEIVER_DESTROYED_SLOT_NAME) == 0) { - QObject *arg = *(QObject**)args[1]; - - //avoid hash changes during the destruction - QHash<int, DynamicSlotData*> copy = m_slotReceivers; - QHash<int, DynamicSlotData*>::iterator i = copy.begin(); - while(i != copy.end()) { - //Remove all refs - int refs = i.value()->hasRefTo(arg); - while(refs) { - disconnectNotify(arg, i.key()); - refs--; - } - i++; - } - return -1; - } - - DynamicSlotData* data = m_slotReceivers.value(id); - if (!data) { - qWarning() << "Unknown global slot, id:" << id; - return -1; - } - - Shiboken::GilState gil; - PyObject* retval = 0; - if (m_shortCircuitSlots.contains(id)) { - retval = data->call(reinterpret_cast<PyObject*>(args[1])); - } else { - QList<QByteArray> paramTypes = slot.parameterTypes(); - Shiboken::AutoDecRef preparedArgs(PyTuple_New(paramTypes.count())); - for (int i = 0, max = paramTypes.count(); i < max; ++i) { - const QByteArray& paramType = paramTypes[i]; - Shiboken::Conversions::SpecificConverter converter(paramType.constData()); - PyTuple_SET_ITEM(preparedArgs.object(), i, converter.toPython(args[i+1])); - } - retval = data->call(preparedArgs); - } - - if (!retval) - PyErr_Print(); - else - Py_DECREF(retval); - - return -1; -} diff --git a/sources/pyside2/libpyside/globalreceiverv2.cpp b/sources/pyside2/libpyside/globalreceiverv2.cpp index 05565e516..43ce50a75 100644 --- a/sources/pyside2/libpyside/globalreceiverv2.cpp +++ b/sources/pyside2/libpyside/globalreceiverv2.cpp @@ -40,15 +40,13 @@ #include "globalreceiverv2.h" #include "dynamicqmetaobject_p.h" #include "pysideweakref.h" +#include "signalmanager.h" -#include <QMetaMethod> -#include <QDebug> -#include <QEvent> -#include <QLinkedList> #include <autodecref.h> #include <gilstate.h> -#include "signalmanager.h" +#include <QtCore/QMetaMethod> +#include <QtCore/QSet> #define RECEIVER_DESTROYED_SLOT_NAME "__receiverDestroyed__(QObject*)" @@ -62,6 +60,7 @@ namespace PySide { class DynamicSlotDataV2 { + Q_DISABLE_COPY(DynamicSlotDataV2) public: DynamicSlotDataV2(PyObject* callback, GlobalReceiverV2* parent); ~DynamicSlotDataV2(); @@ -128,11 +127,11 @@ QByteArray DynamicSlotDataV2::hash() const QByteArray DynamicSlotDataV2::hash(PyObject* callback) { Shiboken::GilState gil; - if (PyMethod_Check(callback)) + if (PyMethod_Check(callback)) { return QByteArray::number((qlonglong)PyObject_Hash(PyMethod_GET_FUNCTION(callback))) + QByteArray::number((qlonglong)PyObject_Hash(PyMethod_GET_SELF(callback))); - else - return QByteArray::number((qlonglong)PyObject_Hash(callback)); + } + return QByteArray::number(qlonglong(PyObject_Hash(callback))); } PyObject* DynamicSlotDataV2::callback() @@ -154,18 +153,15 @@ PyObject* DynamicSlotDataV2::callback() int DynamicSlotDataV2::id(const char* signature) const { - if (m_signatures.contains(signature)) - return m_signatures[signature]; - return -1; + const auto it = m_signatures.constFind(signature); + return it != m_signatures.cend() ? it.value() : -1; } int DynamicSlotDataV2::addSlot(const char* signature) { int index = id(signature); - if (index == -1) { - DynamicQMetaObject *dmo = const_cast<DynamicQMetaObject*>(reinterpret_cast<const DynamicQMetaObject*>(m_parent->metaObject())); - index = m_signatures[signature] = dmo->addSlot(signature); - } + if (index == -1) + index = m_signatures[signature] = m_parent->metaObjectBuilder().addSlot(signature); return index; } @@ -189,8 +185,10 @@ DynamicSlotDataV2::~DynamicSlotDataV2() Py_DECREF(m_callback); } -GlobalReceiverV2::GlobalReceiverV2(PyObject *callback, SharedMap map) - : QObject(0), m_metaObject(GLOBAL_RECEIVER_CLASS_NAME, &QObject::staticMetaObject), m_sharedMap(map) +GlobalReceiverV2::GlobalReceiverV2(PyObject *callback, SharedMap map) : + QObject(nullptr), + m_metaObject(GLOBAL_RECEIVER_CLASS_NAME, &QObject::staticMetaObject), + m_sharedMap(std::move(map)) { m_data = new DynamicSlotDataV2(callback, this); m_metaObject.addSlot(RECEIVER_DESTROYED_SLOT_NAME); @@ -202,7 +200,7 @@ GlobalReceiverV2::GlobalReceiverV2(PyObject *callback, SharedMap map) DESTROY_SIGNAL_ID = QObject::staticMetaObject.indexOfSignal("destroyed(QObject*)"); if (DESTROY_SLOT_ID == 0) - DESTROY_SLOT_ID = m_metaObject.indexOfSlot(RECEIVER_DESTROYED_SLOT_NAME); + DESTROY_SLOT_ID = m_metaObject.indexOfMethod(QMetaMethod::Slot, RECEIVER_DESTROYED_SLOT_NAME); } @@ -251,7 +249,7 @@ void GlobalReceiverV2::incRef(const QObject* link) void GlobalReceiverV2::decRef(const QObject* link) { - if (m_refs.size() <= 0) + if (m_refs.empty()) return; @@ -268,7 +266,7 @@ void GlobalReceiverV2::decRef(const QObject* link) } } - if (m_refs.size() == 0) + if (m_refs.empty()) Py_BEGIN_ALLOW_THREADS delete this; Py_END_ALLOW_THREADS @@ -285,9 +283,9 @@ int GlobalReceiverV2::refCount(const QObject* link) const void GlobalReceiverV2::notify() { - QSet<const QObject*> objs = QSet<const QObject*>::fromList(m_refs); + const auto objSet = QSet<const QObject*>::fromList(m_refs); Py_BEGIN_ALLOW_THREADS - foreach(const QObject* o, objs) { + for (const QObject *o : objSet) { QMetaObject::disconnect(o, DESTROY_SIGNAL_ID, this, DESTROY_SLOT_ID); QMetaObject::connect(o, DESTROY_SIGNAL_ID, this, DESTROY_SLOT_ID); } @@ -306,7 +304,7 @@ QByteArray GlobalReceiverV2::hash(PyObject* callback) const QMetaObject* GlobalReceiverV2::metaObject() const { - return m_metaObject.update(); + return const_cast<GlobalReceiverV2 *>(this)->m_metaObject.update(); } int GlobalReceiverV2::qt_metacall(QMetaObject::Call call, int id, void** args) @@ -328,9 +326,9 @@ int GlobalReceiverV2::qt_metacall(QMetaObject::Call call, int id, void** args) } if (id == DESTROY_SLOT_ID) { - if (m_refs.size() == 0) + if (m_refs.empty()) return -1; - QObject *obj = *(QObject**)args[1]; + QObject *obj = *reinterpret_cast<QObject**>(args[1]); incRef(); //keep the object live (safe ref) m_refs.removeAll(obj); // remove all refs to this object decRef(); //remove the safe ref diff --git a/sources/pyside2/libpyside/globalreceiverv2.h b/sources/pyside2/libpyside/globalreceiverv2.h index 880719d6f..b92be93a8 100644 --- a/sources/pyside2/libpyside/globalreceiverv2.h +++ b/sources/pyside2/libpyside/globalreceiverv2.h @@ -41,15 +41,14 @@ #define GLOBALRECEIVER_V2_H #include <sbkpython.h> -#include <QObject> -#include <QHash> -#include <QSet> -#include <QSharedPointer> -#include <QLinkedList> -#include <QByteArray> #include "dynamicqmetaobject.h" +#include <QtCore/QByteArray> +#include <QtCore/QObject> +#include <QtCore/QMap> +#include <QtCore/QSharedPointer> + namespace PySide { @@ -78,13 +77,13 @@ public: /** * Destructor **/ - ~GlobalReceiverV2(); + ~GlobalReceiverV2() override; /** * Reimplemented function from QObject **/ - int qt_metacall(QMetaObject::Call call, int id, void** args); - const QMetaObject* metaObject() const; + int qt_metacall(QMetaObject::Call call, int id, void** args) override; + const QMetaObject* metaObject() const override; /** * Add a extra slot to this object @@ -122,22 +121,25 @@ public: int refCount(const QObject* link) const; /** - * Use to retrive the unique hash of this GlobalReceiver object + * Use to retrieve the unique hash of this GlobalReceiver object * * @return a string with a unique id based on GlobalReceiver contents **/ QByteArray hash() const; /** - * Use to retrive the unique hash of the PyObject based on GlobalReceiver rules + * Use to retrieve the unique hash of the PyObject based on GlobalReceiver rules * * @param callback The Python callable object used to calculate the id * @return a string with a unique id based on GlobalReceiver contents **/ static QByteArray hash(PyObject* callback); + const MetaObjectBuilder &metaObjectBuilder() const { return m_metaObject; } + MetaObjectBuilder &metaObjectBuilder() { return m_metaObject; } + private: - DynamicQMetaObject m_metaObject; + MetaObjectBuilder m_metaObject; DynamicSlotDataV2 *m_data; QList<const QObject*> m_refs; SharedMap m_sharedMap; diff --git a/sources/pyside2/libpyside/pyside.cpp b/sources/pyside2/libpyside/pyside.cpp index b4f7d8771..6e4a3efd4 100644 --- a/sources/pyside2/libpyside/pyside.cpp +++ b/sources/pyside2/libpyside/pyside.cpp @@ -38,6 +38,7 @@ ****************************************************************************/ #include "pyside.h" +#include "pyside_p.h" #include "signalmanager.h" #include "pysideclassinfo_p.h" #include "pysideproperty_p.h" @@ -51,23 +52,24 @@ #include "destroylistener.h" #include <autodecref.h> -#include <qapp_macro.h> #include <basewrapper.h> +#include <bindingmanager.h> +#include <gilstate.h> #include <sbkconverter.h> #include <sbkstring.h> -#include <gilstate.h> -#include <bindingmanager.h> +#include <qapp_macro.h> + +#include <QtCore/QByteArray> +#include <QtCore/QCoreApplication> +#include <QtCore/QDir> +#include <QtCore/QFileInfo> +#include <QtCore/QSharedPointer> +#include <QtCore/QStack> + #include <algorithm> -#include <typeinfo> #include <cstring> #include <cctype> -#include <QByteArray> -#include <QCoreApplication> -#include <QDebug> -#include <QDir> -#include <QFileInfo> -#include <QSharedPointer> -#include <QStack> +#include <typeinfo> static QStack<PySide::CleanupFunction> cleanupFunctionList; static void* qobjectNextAddr; @@ -188,29 +190,21 @@ void destroyQCoreApplication() MakeSingletonQAppWrapper(NULL); } -struct TypeUserData { - TypeUserData(PyTypeObject* type, const QMetaObject* metaobject) : mo(type, metaobject) {} - DynamicQMetaObject mo; - std::size_t cppObjSize; -}; - std::size_t getSizeOfQObject(SbkObjectType* type) { - using namespace Shiboken::ObjectType; - TypeUserData* userData = reinterpret_cast<TypeUserData*>(getTypeUserData(reinterpret_cast<SbkObjectType*>(type))); - return userData->cppObjSize; + return retrieveTypeUserData(type)->cppObjSize; } -void initDynamicMetaObject(SbkObjectType* type, const QMetaObject* base, const std::size_t& cppObjSize) +void initDynamicMetaObject(SbkObjectType* type, const QMetaObject* base, std::size_t cppObjSize) { //create DynamicMetaObject based on python type - TypeUserData* userData = new TypeUserData(reinterpret_cast<PyTypeObject*>(type), base); - userData->cppObjSize = cppObjSize; + auto userData = + new TypeUserData(reinterpret_cast<PyTypeObject*>(type), base, cppObjSize); userData->mo.update(); Shiboken::ObjectType::setTypeUserData(type, userData, Shiboken::callCppDestructor<TypeUserData>); //initialize staticQMetaObject property - void* metaObjectPtr = &userData->mo; + void *metaObjectPtr = const_cast<QMetaObject *>(userData->mo.update()); static SbkConverter* converter = Shiboken::Conversions::getConverter("QMetaObject"); if (!converter) return; @@ -218,6 +212,36 @@ void initDynamicMetaObject(SbkObjectType* type, const QMetaObject* base, const s PyObject_SetAttrString(reinterpret_cast<PyObject*>(type), "staticMetaObject", pyMetaObject); } +TypeUserData *retrieveTypeUserData(SbkObjectType *sbkTypeObj) +{ + return reinterpret_cast<TypeUserData *>(Shiboken::ObjectType::getTypeUserData(sbkTypeObj)); +} + +TypeUserData *retrieveTypeUserData(PyTypeObject *pyTypeObj) +{ + return retrieveTypeUserData(reinterpret_cast<SbkObjectType *>(pyTypeObj)); +} + +TypeUserData *retrieveTypeUserData(PyObject *pyObj) +{ + auto pyTypeObj = PyType_Check(pyObj) + ? reinterpret_cast<PyTypeObject *>(pyObj) : Py_TYPE(pyObj); + return retrieveTypeUserData(pyTypeObj); +} + +const QMetaObject *retrieveMetaObject(PyTypeObject *pyTypeObj) +{ + TypeUserData *userData = retrieveTypeUserData(pyTypeObj); + return userData ? userData->mo.update() : nullptr; +} + +const QMetaObject *retrieveMetaObject(PyObject *pyObj) +{ + auto pyTypeObj = PyType_Check(pyObj) + ? reinterpret_cast<PyTypeObject *>(pyObj) : Py_TYPE(pyObj); + return retrieveMetaObject(pyTypeObj); +} + void initDynamicMetaObject(SbkObjectType* type, const QMetaObject* base) { initDynamicMetaObject(type, base, 0); @@ -230,25 +254,21 @@ void initQObjectSubType(SbkObjectType *type, PyObject *args, PyObject * /* kwds PyObject* bases = PyTuple_GET_ITEM(args, 1); int numBases = PyTuple_GET_SIZE(bases); - QMetaObject* baseMo = 0; - SbkObjectType* qobjBase = 0; + + TypeUserData *userData = nullptr; for (int i = 0; i < numBases; ++i) { PyTypeObject* base = reinterpret_cast<PyTypeObject*>(PyTuple_GET_ITEM(bases, i)); if (PyType_IsSubtype(base, qObjType)) { - baseMo = reinterpret_cast<QMetaObject*>(Shiboken::ObjectType::getTypeUserData(reinterpret_cast<SbkObjectType*>(base))); - qobjBase = reinterpret_cast<SbkObjectType*>(base); - reinterpret_cast<DynamicQMetaObject*>(baseMo)->update(); + userData = retrieveTypeUserData(base); break; } } - if (!baseMo) { + if (!userData) { qWarning("Sub class of QObject not inheriting QObject!? Crash will happen when using %s.", className.constData()); return; } - - TypeUserData* userData = reinterpret_cast<TypeUserData*>(Shiboken::ObjectType::getTypeUserData(qobjBase)); - initDynamicMetaObject(type, baseMo, userData->cppObjSize); + initDynamicMetaObject(type, userData->mo.update(), userData->cppObjSize); } PyObject* getMetaDataFromQObject(QObject* cppSelf, PyObject* self, PyObject* name) @@ -299,7 +319,7 @@ PyObject* getMetaDataFromQObject(QObject* cppSelf, PyObject* self, PyObject* nam } } } - if (signalList.size() > 0) { + if (!signalList.empty()) { PyObject* pySignal = reinterpret_cast<PyObject*>(Signal::newObjectFromMethod(self, signalList)); PyObject_SetAttr(self, name, pySignal); return pySignal; @@ -366,7 +386,7 @@ PyObject* getWrapperForQObject(QObject* cppSelf, SbkObjectType* sbk_type) // set and check if it's created after the set call QVariant existing = cppSelf->property(invalidatePropertyName); if (!existing.isValid()) { - QSharedPointer<any_t> shared_with_del((any_t*)cppSelf, invalidatePtr); + QSharedPointer<any_t> shared_with_del(reinterpret_cast<any_t*>(cppSelf), invalidatePtr); cppSelf->setProperty(invalidatePropertyName, QVariant::fromValue(shared_with_del)); pyOut = reinterpret_cast<PyObject *>(Shiboken::BindingManager::instance().retrieveWrapper(cppSelf)); if (pyOut) { diff --git a/sources/pyside2/libpyside/pyside.h b/sources/pyside2/libpyside/pyside.h index e2e108ed8..b53048eba 100644 --- a/sources/pyside2/libpyside/pyside.h +++ b/sources/pyside2/libpyside/pyside.h @@ -41,16 +41,15 @@ #define PYSIDE_H #include <sbkpython.h> + #include <pysidemacros.h> #ifdef PYSIDE_QML_SUPPORT -# include <qqml.h> +# include <QtQml/qqml.h> #endif -#include <QMetaType> -#include <QHash> -#include <QList> -#include <QLoggingCategory> +#include <QtCore/QMetaType> +#include <QtCore/QHash> struct SbkObjectType; @@ -101,7 +100,8 @@ struct initQtMetaType<T, false> { }; PYSIDE_DEPRECATED(PYSIDE_API void initDynamicMetaObject(SbkObjectType* type, const QMetaObject* base)); -PYSIDE_API void initDynamicMetaObject(SbkObjectType* type, const QMetaObject* base, const std::size_t& cppObjSize); +PYSIDE_API void initDynamicMetaObject(SbkObjectType* type, const QMetaObject* base, + std::size_t cppObjSize); PYSIDE_API void initQObjectSubType(SbkObjectType* type, PyObject* args, PyObject* kwds); /// Return the size in bytes of a type that inherits QObject. diff --git a/sources/pyside2/libpyside/globalreceiver.h b/sources/pyside2/libpyside/pyside_p.h index 426d40bfe..1084a40a1 100644 --- a/sources/pyside2/libpyside/globalreceiver.h +++ b/sources/pyside2/libpyside/pyside_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt for Python. @@ -37,44 +37,35 @@ ** ****************************************************************************/ -#ifndef GLOBALRECEIVER_H -#define GLOBALRECEIVER_H +#ifndef PYSIDE_P_H +#define PYSIDE_P_H -#include <sbkpython.h> -#include <QObject> -#include <QHash> -#include <QSet> -#include "dynamicqmetaobject.h" +#include <pysidemacros.h> -namespace PySide -{ +#include <dynamicqmetaobject.h> -class DynamicSlotData; +struct SbkObjectType; -class GlobalReceiver : public QObject +namespace PySide { -public: - GlobalReceiver(); - ~GlobalReceiver(); - int qt_metacall(QMetaObject::Call call, int id, void** args); - const QMetaObject* metaObject() const; - int addSlot(const char* slot, PyObject* callback); - void removeSlot(int slotId); - void connectNotify(QObject* sender, int slotId); - void disconnectNotify(QObject* sender, int slotId); - bool hasConnectionWith(const QObject* object); -protected: - using QObject::connectNotify; - using QObject::disconnectNotify; +// Struct associated with QObject's via Shiboken::Object::getTypeUserData() +struct TypeUserData +{ + explicit TypeUserData(PyTypeObject* type, const QMetaObject* metaobject, std::size_t size) : + mo(type, metaobject), cppObjSize(size) {} -private: - DynamicQMetaObject m_metaObject; - QSet<int> m_shortCircuitSlots; - QHash<int, DynamicSlotData* > m_slotReceivers; + MetaObjectBuilder mo; + std::size_t cppObjSize; }; -} +TypeUserData *retrieveTypeUserData(SbkObjectType *sbkTypeObj); +TypeUserData *retrieveTypeUserData(PyTypeObject *pyTypeObj); +TypeUserData *retrieveTypeUserData(PyObject *pyObj); +// For QML +PYSIDE_API const QMetaObject *retrieveMetaObject(PyTypeObject *pyTypeObj); +PYSIDE_API const QMetaObject *retrieveMetaObject(PyObject *pyObj); -#endif +} //namespace PySide +#endif // PYSIDE_P_H diff --git a/sources/pyside2/libpyside/pysideclassinfo.cpp b/sources/pyside2/libpyside/pysideclassinfo.cpp index 5593825c3..4edf0fa91 100644 --- a/sources/pyside2/libpyside/pysideclassinfo.cpp +++ b/sources/pyside2/libpyside/pysideclassinfo.cpp @@ -38,12 +38,13 @@ ****************************************************************************/ #include <sbkpython.h> + #include "pysideclassinfo.h" +#include "pyside_p.h" #include "pysideclassinfo_p.h" #include "dynamicqmetaobject.h" #include <shiboken.h> -#include <QDebug> #define CLASSINFO_CLASS_NAME "ClassInfo" @@ -74,9 +75,8 @@ static PyType_Spec PySideClassInfoType_spec = { PyTypeObject *PySideClassInfoTypeF(void) { - static PyTypeObject *type = nullptr; - if (!type) - type = (PyTypeObject *)PyType_FromSpec(&PySideClassInfoType_spec); + static PyTypeObject *type = + reinterpret_cast<PyTypeObject *>(PyType_FromSpec(&PySideClassInfoType_spec)); return type; } @@ -97,8 +97,7 @@ PyObject *classCall(PyObject *self, PyObject *args, PyObject * /* kw */) return 0; } - PyObject* klass; - klass = PyTuple_GetItem(args, 0); + PyObject *klass = PyTuple_GetItem(args, 0); bool validClass = false; // This will sometimes segfault if you mistakenly use it on a function declaration @@ -107,10 +106,11 @@ PyObject *classCall(PyObject *self, PyObject *args, PyObject * /* kw */) return 0; } - if (Shiboken::ObjectType::checkType(reinterpret_cast<PyTypeObject*>(klass))) { - PySide::DynamicQMetaObject* mo = reinterpret_cast<PySide::DynamicQMetaObject*>(Shiboken::ObjectType::getTypeUserData(reinterpret_cast<SbkObjectType*>(klass))); - if (mo) { - mo->addInfo(PySide::ClassInfo::getMap(data)); + PyTypeObject *klassType = reinterpret_cast<PyTypeObject*>(klass); + if (Shiboken::ObjectType::checkType(klassType)) { + if (auto userData = PySide::retrieveTypeUserData(klassType)) { + PySide::MetaObjectBuilder &mo = userData->mo; + mo.addInfo(PySide::ClassInfo::getMap(data)); pData->m_alreadyWrapped = true; validClass = true; } diff --git a/sources/pyside2/libpyside/pysideclassinfo.h b/sources/pyside2/libpyside/pysideclassinfo.h index 910dd9f82..ff60b91c3 100644 --- a/sources/pyside2/libpyside/pysideclassinfo.h +++ b/sources/pyside2/libpyside/pysideclassinfo.h @@ -41,9 +41,11 @@ #define PYSIDE_CLASSINFO_H #include <pysidemacros.h> + #include <sbkpython.h> -#include <QMap> -#include <QByteArray> + +#include <QtCore/QMap> +#include <QtCore/QByteArray> extern "C" { diff --git a/sources/pyside2/libpyside/pysidemetafunction.cpp b/sources/pyside2/libpyside/pysidemetafunction.cpp index a9fbbc7fc..4cdc7ec16 100644 --- a/sources/pyside2/libpyside/pysidemetafunction.cpp +++ b/sources/pyside2/libpyside/pysidemetafunction.cpp @@ -36,14 +36,13 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ -#include <sbkpython.h> + #include "pysidemetafunction.h" #include "pysidemetafunction_p.h" #include <shiboken.h> -#include <QObject> -#include <QMetaMethod> -#include <QDebug> + +#include <QtCore/QMetaMethod> extern "C" { diff --git a/sources/pyside2/libpyside/pysidemetafunction.h b/sources/pyside2/libpyside/pysidemetafunction.h index 020f02d49..1085ecb5e 100644 --- a/sources/pyside2/libpyside/pysidemetafunction.h +++ b/sources/pyside2/libpyside/pysidemetafunction.h @@ -40,13 +40,12 @@ #ifndef PYSIDE_METAFUNCTION_H #define PYSIDE_METAFUNCTION_H -#include <QObject> -#include <QString> -#include <QStringList> - #include <pysidemacros.h> + #include <sbkpython.h> +#include <QtCore/QObject> + extern "C" { extern PYSIDE_API PyTypeObject *PySideMetaFunctionTypeF(void); diff --git a/sources/pyside2/libpyside/pysidemetafunction_p.h b/sources/pyside2/libpyside/pysidemetafunction_p.h index c3b8fe0c7..442e05ea7 100644 --- a/sources/pyside2/libpyside/pysidemetafunction_p.h +++ b/sources/pyside2/libpyside/pysidemetafunction_p.h @@ -41,8 +41,8 @@ #define PYSIDE_METAFUNCTION_P_H #include <sbkpython.h> -#include <QList> -#include <QByteArray> + +#include <QtCore/QtGlobal> QT_BEGIN_NAMESPACE class QObject; diff --git a/sources/pyside2/libpyside/pysideproperty.cpp b/sources/pyside2/libpyside/pysideproperty.cpp index 279e09ec1..091b0c447 100644 --- a/sources/pyside2/libpyside/pysideproperty.cpp +++ b/sources/pyside2/libpyside/pysideproperty.cpp @@ -45,8 +45,6 @@ #include "pysidesignal_p.h" #include <shiboken.h> -#include <QDebug> - #define QPROPERTY_CLASS_NAME "Property" @@ -173,7 +171,8 @@ int qpropertyTpInit(PyObject* self, PyObject* args, PyObject* kwds) "designable", "scriptable", "stored", "user", "constant", "final", 0}; if (!PyArg_ParseTupleAndKeywords(args, kwds, - "O|OOOOsObbbbbb:QtCore.QProperty", (char**) kwlist, + "O|OOOOsObbbbbb:QtCore.QProperty", + const_cast<char**>(kwlist), /*OO*/ &type, &(pData->fget), /*OOO*/ &(pData->fset), &(pData->freset), &(pData->fdel), /*s*/ &(pData->doc), @@ -197,14 +196,13 @@ int qpropertyTpInit(PyObject* self, PyObject* args, PyObject* kwds) Py_XINCREF(pData->fdel); Py_XINCREF(pData->notify); return 1; - } else { - pData->fget = 0; - pData->fset = 0; - pData->freset = 0; - pData->fdel = 0; - pData->notify = 0; - return -1; } + pData->fget = nullptr; + pData->fset = nullptr; + pData->freset = nullptr; + pData->fdel = nullptr; + pData->notify = nullptr; + return -1; } void qpropertyDeAlloc(PyObject* self) @@ -225,10 +223,9 @@ PyObject *qPropertyCall(PyObject *self, PyObject *args, PyObject * /* kw */) Py_INCREF(self); return self; - } else { - PyErr_SetString(PyExc_TypeError, "Invalid property usage."); - return 0; } + PyErr_SetString(PyExc_TypeError, "Invalid property usage."); + return nullptr; } PyObject* qPropertySetter(PyObject* self, PyObject* callback) @@ -242,10 +239,9 @@ PyObject* qPropertySetter(PyObject* self, PyObject* callback) Py_INCREF(callback); return callback; - } else { - PyErr_SetString(PyExc_TypeError, "Invalid property setter agument."); - return 0; } + PyErr_SetString(PyExc_TypeError, "Invalid property setter agument."); + return nullptr; } PyObject* qPropertyGetter(PyObject* self, PyObject* callback) @@ -259,10 +255,9 @@ PyObject* qPropertyGetter(PyObject* self, PyObject* callback) Py_INCREF(callback); return callback; - } else { - PyErr_SetString(PyExc_TypeError, "Invalid property getter agument."); - return 0; } + PyErr_SetString(PyExc_TypeError, "Invalid property getter agument."); + return nullptr; } static int qpropertyTraverse(PyObject* self, visitproc visit, void* arg) diff --git a/sources/pyside2/libpyside/pysideproperty.h b/sources/pyside2/libpyside/pysideproperty.h index d77416abe..0ea5e84d6 100644 --- a/sources/pyside2/libpyside/pysideproperty.h +++ b/sources/pyside2/libpyside/pysideproperty.h @@ -41,8 +41,10 @@ #define PYSIDE_PROPERTY_H #include <pysidemacros.h> + #include <sbkpython.h> -#include <QObject> + +#include <QtCore/QMetaObject> extern "C" { diff --git a/sources/pyside2/libpyside/pysideqflags.cpp b/sources/pyside2/libpyside/pysideqflags.cpp index 684628e57..cb57031b0 100644 --- a/sources/pyside2/libpyside/pysideqflags.cpp +++ b/sources/pyside2/libpyside/pysideqflags.cpp @@ -38,8 +38,9 @@ ****************************************************************************/ #include "pysideqflags.h" -#include <sbkenum.h> + #include <autodecref.h> +#include <sbkenum.h> extern "C" { struct SbkConverter; @@ -174,13 +175,8 @@ namespace QFlags newspec->itemsize = SbkNewQFlagsType_spec.itemsize; newspec->flags = SbkNewQFlagsType_spec.flags; int idx = -1; -#ifdef IS_PY3K -# define SLOT slot -#else -# define SLOT slot_ -#endif - while (numberMethods[++idx].SLOT) { - assert(SbkNewQFlagsType_slots[idx].SLOT == numberMethods[idx].SLOT); + while (numberMethods[++idx].slot) { + assert(SbkNewQFlagsType_slots[idx].slot == numberMethods[idx].slot); SbkNewQFlagsType_slots[idx].pfunc = numberMethods[idx].pfunc; } newspec->slots = SbkNewQFlagsType_spec.slots; diff --git a/sources/pyside2/libpyside/pysidesignal.cpp b/sources/pyside2/libpyside/pysidesignal.cpp index c3dc65968..e7fd389a8 100644 --- a/sources/pyside2/libpyside/pysidesignal.cpp +++ b/sources/pyside2/libpyside/pysidesignal.cpp @@ -43,7 +43,12 @@ #include "signalmanager.h" #include <shiboken.h> -#include <QDebug> + +#include <QtCore/QObject> +#include <QtCore/QMetaMethod> +#include <QtCore/QMetaObject> + +#include <utility> #define SIGNAL_CLASS_NAME "Signal" #define SIGNAL_INSTANCE_NAME "SignalInstance" @@ -54,14 +59,15 @@ namespace Signal { //aux class SignalSignature { public: - SignalSignature() : m_attributes(QMetaMethod::Compatibility) {} - SignalSignature(QByteArray parameterTypes) : m_parameterTypes(parameterTypes), - m_attributes(QMetaMethod::Compatibility) {} - SignalSignature(QByteArray parameterTypes, QMetaMethod::Attributes attributes) : - m_parameterTypes(parameterTypes), + SignalSignature() = default; + explicit SignalSignature(QByteArray parameterTypes) : + m_parameterTypes(std::move(parameterTypes)) {} + explicit SignalSignature(QByteArray parameterTypes, QMetaMethod::Attributes attributes) : + m_parameterTypes(std::move(parameterTypes)), m_attributes(attributes) {} + QByteArray m_parameterTypes; - QMetaMethod::Attributes m_attributes; + QMetaMethod::Attributes m_attributes = QMetaMethod::Compatibility; }; static char* buildSignature(const char*, const char*); @@ -412,8 +418,7 @@ PyObject* signalInstanceConnect(PyObject* self, PyObject* args, PyObject* kwds) PyObject* result = PyObject_CallObject(pyMethod, tupleArgs); if (result == Py_True || result == Py_False) return result; - else - Py_XDECREF(result); + Py_XDECREF(result); } if (!PyErr_Occurred()) // PYSIDE-79: inverse the logic. A Null return needs an error. PyErr_Format(PyExc_RuntimeError, "Failed to connect signal %s.", source->d->signature); @@ -661,9 +666,10 @@ char* getTypeName(PyObject* type) typeName = strdup("PyObject"); } return typeName; - } else if (type == Py_None) { // Must be checked before as Shiboken::String::check accepts Py_None + } + if (type == Py_None) // Must be checked before as Shiboken::String::check accepts Py_None return strdup("void"); - } else if (Shiboken::String::check(type)) { + if (Shiboken::String::check(type)) { const char *result = Shiboken::String::toCString(type); if (!strcmp(result, "qreal")) result = sizeof(qreal) == sizeof(double) ? "double" : "float"; @@ -778,7 +784,7 @@ PySideSignalInstance* newObjectFromMethod(PyObject* source, const QList<QMetaMet { PySideSignalInstance* root = 0; PySideSignalInstance* previous = 0; - foreach (const QMetaMethod &m, methodList) { + for (const QMetaMethod &m : methodList) { PySideSignalInstance* item = PyObject_New(PySideSignalInstance, PySideSignalInstanceTypeF()); if (!root) root = item; @@ -835,7 +841,7 @@ template<typename T> static typename T::value_type join(T t, const char* sep) { typename T::value_type res; - if (!t.size()) + if (t.isEmpty()) return res; typename T::const_iterator it = t.begin(); @@ -894,7 +900,7 @@ void registerSignals(SbkObjectType* pyObj, const QMetaObject* metaObject) self->signaturesSize = 0; self->signatures = 0; self->signatureAttributes = 0; - self->initialized = 0; + self->initialized = false; self->homonymousMethod = 0; // Empty signatures comes first! So they will be the default signal signature @@ -949,9 +955,9 @@ QStringList getArgsFromSignature(const char* signature, bool* isShortCircuit) if (isShortCircuit) *isShortCircuit = !qsignature.contains(QLatin1Char('(')); - if (qsignature.contains(QLatin1String("()")) || qsignature.contains(QLatin1String("(void)"))) { + if (qsignature.contains(QLatin1String("()")) || qsignature.contains(QLatin1String("(void)"))) return result; - } else if (qsignature.contains(QLatin1Char('('))) { + if (qsignature.contains(QLatin1Char('('))) { static QRegExp regex(QLatin1String(".+\\((.*)\\)")); //get args types QString types = qsignature; @@ -1047,9 +1053,8 @@ QString codeCallbackName(PyObject* callback, const QString& funcName) PyObject* self = PyMethod_GET_SELF(callback); PyObject* func = PyMethod_GET_FUNCTION(callback); return funcName + QString::number(quint64(self), 16) + QString::number(quint64(func), 16); - } else { - return funcName + QString::number(quint64(callback), 16); } + return funcName + QString::number(quint64(callback), 16); } } //namespace Signal diff --git a/sources/pyside2/libpyside/pysidesignal.h b/sources/pyside2/libpyside/pysidesignal.h index abbefbb1a..a2d58a27c 100644 --- a/sources/pyside2/libpyside/pysidesignal.h +++ b/sources/pyside2/libpyside/pysidesignal.h @@ -40,14 +40,19 @@ #ifndef PYSIDE_SIGNAL_H #define PYSIDE_SIGNAL_H -#include <QObject> -#include <QString> -#include <QStringList> - #include <pysidemacros.h> + #include <sbkpython.h> #include <basewrapper.h> +#include <QtCore/QList> +#include <QtCore/QMetaMethod> + +QT_BEGIN_NAMESPACE +struct QMetaObject; +class QObject; +QT_END_NAMESPACE + extern "C" { extern PYSIDE_API PyTypeObject *PySideSignalTypeF(void); diff --git a/sources/pyside2/libpyside/pysideslot.cpp b/sources/pyside2/libpyside/pysideslot.cpp index 6ae664c42..6f6658cf8 100644 --- a/sources/pyside2/libpyside/pysideslot.cpp +++ b/sources/pyside2/libpyside/pysideslot.cpp @@ -42,8 +42,9 @@ #include "pysideslot_p.h" #include <shiboken.h> -#include <QString> -#include <QMetaObject> + +#include <QtCore/QMetaObject> +#include <QtCore/QString> #define SLOT_DEC_NAME "Slot" @@ -96,8 +97,10 @@ int slotTpInit(PyObject *self, PyObject *args, PyObject *kw) if (emptyTuple == 0) emptyTuple = PyTuple_New(0); - if (!PyArg_ParseTupleAndKeywords(emptyTuple, kw, "|sO:QtCore." SLOT_DEC_NAME, (char**) kwlist, &argName, &argResult)) + if (!PyArg_ParseTupleAndKeywords(emptyTuple, kw, "|sO:QtCore." SLOT_DEC_NAME, + const_cast<char**>(kwlist), &argName, &argResult)) { return 0; + } PySideSlot *data = reinterpret_cast<PySideSlot*>(self); for(Py_ssize_t i = 0, i_max = PyTuple_Size(args); i < i_max; i++) { diff --git a/sources/pyside2/libpyside/pysideweakref.cpp b/sources/pyside2/libpyside/pysideweakref.cpp index 6c38d39c4..6b5073db8 100644 --- a/sources/pyside2/libpyside/pysideweakref.cpp +++ b/sources/pyside2/libpyside/pysideweakref.cpp @@ -65,7 +65,7 @@ static PyType_Spec PySideCallableObjectType_spec = { }; -static PyTypeObject *PySideCallableObjectTypeF(void) +static PyTypeObject *PySideCallableObjectTypeF() { static PyTypeObject *type = (PyTypeObject *)PyType_FromSpec(&PySideCallableObjectType_spec); diff --git a/sources/pyside2/libpyside/signalmanager.cpp.in b/sources/pyside2/libpyside/signalmanager.cpp index c67bc6369..8925ffd35 100644 --- a/sources/pyside2/libpyside/signalmanager.cpp.in +++ b/sources/pyside2/libpyside/signalmanager.cpp @@ -43,25 +43,25 @@ #include "pysideproperty.h" #include "pysideproperty_p.h" #include "pyside.h" +#include "pyside_p.h" #include "dynamicqmetaobject.h" #include "pysidemetafunction_p.h" -#include <QtCore> -#include <QHash> -#include <QStringList> -#include <QMetaMethod> #include <autodecref.h> -#include <gilstate.h> -#include <QDebug> -#include <limits> -#include <algorithm> #include <basewrapper.h> #include <bindingmanager.h> +#include <gilstate.h> #include <sbkconverter.h> #include <sbkstring.h> +#include <QtCore/QDebug> +#include <QtCore/QHash> + +#include <algorithm> +#include <limits> + // These private headers are needed to throw JavaScript exceptions -#if @QML_PRIVATE_API_SUPPORT@ +#if PYSIDE_QML_PRIVATE_API_SUPPORT #include <private/qv4engine_p.h> #include <private/qv4context_p.h> #include <private/qqmldata_p.h> @@ -76,7 +76,6 @@ #define PYSIDE_SLOT '1' #define PYSIDE_SIGNAL '2' #include "globalreceiverv2.h" -#include "globalreceiver.h" #define PYTHON_TYPE "PyObject" @@ -91,7 +90,7 @@ namespace { static void destroyMetaObject(PyObject* obj) { void* ptr = PyCapsule_GetPointer(obj, 0); - PySide::DynamicQMetaObject* meta = reinterpret_cast<PySide::DynamicQMetaObject*>(ptr); + auto meta = reinterpret_cast<PySide::MetaObjectBuilder*>(ptr); SbkObject* wrapper = Shiboken::BindingManager::instance().retrieveWrapper(meta); if (wrapper) Shiboken::BindingManager::instance().releaseWrapper(wrapper); @@ -101,7 +100,7 @@ namespace { #else static void destroyMetaObject(void* obj) { - PySide::DynamicQMetaObject* meta = reinterpret_cast<PySide::DynamicQMetaObject*>(obj); + auto meta = reinterpret_cast<PySide::MetaObjectBuilder*>(obj); SbkObject* wrapper = Shiboken::BindingManager::instance().retrieveWrapper(meta); if (wrapper) Shiboken::BindingManager::instance().releaseWrapper(wrapper); @@ -142,11 +141,16 @@ PyObjectWrapper::~PyObjectWrapper() Py_XDECREF(m_me); } -PyObjectWrapper& PyObjectWrapper::operator=(const PySide::PyObjectWrapper& other) +void PyObjectWrapper::reset(PyObject *o) { - Py_XINCREF(other.m_me); + Py_XINCREF(o); Py_XDECREF(m_me); - m_me = other.m_me; + m_me = o; +} + +PyObjectWrapper& PyObjectWrapper::operator=(const PySide::PyObjectWrapper& other) +{ + reset(other.m_me); return *this; } @@ -205,10 +209,9 @@ QDataStream &operator>>(QDataStream& in, PyObjectWrapper& myObj) in >> repr; Shiboken::AutoDecRef pyCode(PyBytes_FromStringAndSize(repr.data(), repr.size())); Shiboken::AutoDecRef value(PyObject_CallFunctionObjArgs(eval_func, pyCode.object(), 0)); - if (!value.object()) { - value = Py_None; - } - myObj = PyObjectWrapper(value); + if (!value.object()) + value.reset(Py_None); + myObj.reset(value); return in; } @@ -220,9 +223,6 @@ struct SignalManager::SignalManagerPrivate { SharedMap m_globalReceivers; - //Deprecated - GlobalReceiver m_globalReceiver; - SignalManagerPrivate() { m_globalReceivers = SharedMap( new QMap<QByteArray, GlobalReceiverV2*>() ); @@ -304,44 +304,21 @@ SignalManager& SignalManager::instance() return me; } -QObject* SignalManager::globalReceiver() -{ - return &m_d->m_globalReceiver; -} - -void SignalManager::globalReceiverConnectNotify(QObject* source, int slotIndex) -{ - m_d->m_globalReceiver.connectNotify(source, slotIndex); -} - -void SignalManager::globalReceiverDisconnectNotify(QObject* source, int slotIndex) -{ - m_d->m_globalReceiver.disconnectNotify(source, slotIndex); -} - -void SignalManager::addGlobalSlot(const char* slot, PyObject* callback) -{ - addGlobalSlotGetIndex(slot, callback); -} - -int SignalManager::addGlobalSlotGetIndex(const char* slot, PyObject* callback) -{ - return m_d->m_globalReceiver.addSlot(slot, callback); -} - QObject* SignalManager::globalReceiver(QObject *sender, PyObject *callback) { SharedMap globalReceivers = m_d->m_globalReceivers; QByteArray hash = GlobalReceiverV2::hash(callback); GlobalReceiverV2* gr = 0; - if (!globalReceivers->contains(hash)) { - gr = (*globalReceivers)[hash] = new GlobalReceiverV2(callback, globalReceivers); + auto it = globalReceivers->find(hash); + if (it == globalReceivers->end()) { + gr = new GlobalReceiverV2(callback, globalReceivers); + globalReceivers->insert(hash, gr); if (sender) { gr->incRef(sender); // create a link reference gr->decRef(); // remove extra reference } } else { - gr = (*globalReceivers)[hash]; + gr = it.value(); if (sender) gr->incRef(sender); } @@ -465,7 +442,7 @@ int SignalManager::qt_metacall(QObject* object, QMetaObject::Call call, int id, if (PyErr_Occurred()) { -#if @QML_PRIVATE_API_SUPPORT@ +#if PYSIDE_QML_PRIVATE_API_SUPPORT // This JS engine grabber based off of Qt 5.5's `qjsEngine` function QQmlData *data = QQmlData::get(object, false); @@ -572,9 +549,26 @@ bool SignalManager::registerMetaMethod(QObject* source, const char* signature, Q return (ret != -1); } +static MetaObjectBuilder *metaBuilderFromDict(PyObject* dict) +{ + if (!dict || !PyDict_Contains(dict, metaObjectAttr)) + return nullptr; + + PyObject *pyBuilder = PyDict_GetItem(dict, metaObjectAttr); +#ifdef IS_PY3K + return reinterpret_cast<MetaObjectBuilder *>(PyCapsule_GetPointer(pyBuilder, nullptr)); +#else + return reinterpret_cast<MetaObjectBuilder *>(PyCObject_AsVoidPtr(pyBuilder)); +#endif +} + int SignalManager::registerMetaMethodGetIndex(QObject* source, const char* signature, QMetaMethod::MethodType type) { - Q_ASSERT(source); + if (!source) { + qWarning("SignalManager::registerMetaMethodGetIndex(\"%s\") called with source=nullptr.", + signature); + return -1; + } const QMetaObject* metaObject = source->metaObject(); int methodIndex = metaObject->indexOfMethod(signature); // Create the dynamic signal is needed @@ -584,13 +578,13 @@ int SignalManager::registerMetaMethodGetIndex(QObject* source, const char* signa qWarning() << "Invalid Signal signature:" << signature; return -1; } else { - DynamicQMetaObject *dmo = 0; PyObject *pySelf = reinterpret_cast<PyObject*>(self); PyObject* dict = self->ob_dict; + MetaObjectBuilder *dmo = metaBuilderFromDict(dict); // Create a instance meta object - if (!dict || !PyDict_Contains(dict, metaObjectAttr)) { - dmo = new DynamicQMetaObject(Py_TYPE(pySelf), metaObject); + if (!dmo) { + dmo = new MetaObjectBuilder(Py_TYPE(pySelf), metaObject); #ifdef IS_PY3K PyObject* pyDmo = PyCapsule_New(dmo, 0, destroyMetaObject); #else @@ -599,8 +593,6 @@ int SignalManager::registerMetaMethodGetIndex(QObject* source, const char* signa PyObject_SetAttr(pySelf, metaObjectAttr, pyDmo); Py_DECREF(pyDmo); - } else { - dmo = reinterpret_cast<DynamicQMetaObject*>(const_cast<QMetaObject*>(metaObject)); } if (type == QMetaMethod::Signal) @@ -612,32 +604,16 @@ int SignalManager::registerMetaMethodGetIndex(QObject* source, const char* signa return methodIndex; } -bool SignalManager::hasConnectionWith(const QObject *object) -{ - return m_d->m_globalReceiver.hasConnectionWith(object); -} - -const QMetaObject* SignalManager::retriveMetaObject(PyObject *self) +const QMetaObject* SignalManager::retrieveMetaObject(PyObject *self) { Shiboken::GilState gil; - DynamicQMetaObject *mo = 0; Q_ASSERT(self); - PyObject* dict = reinterpret_cast<SbkObject*>(self)->ob_dict; - if (dict && PyDict_Contains(dict, metaObjectAttr)) { - PyObject *pyMo = PyDict_GetItem(dict, metaObjectAttr); - -#ifdef IS_PY3K - mo = reinterpret_cast<DynamicQMetaObject*>(PyCapsule_GetPointer(pyMo, 0)); -#else - mo = reinterpret_cast<DynamicQMetaObject*>(PyCObject_AsVoidPtr(pyMo)); -#endif - } else { - mo = reinterpret_cast<DynamicQMetaObject*>(Shiboken::Object::getTypeUserData(reinterpret_cast<SbkObject*>(self))); - } + MetaObjectBuilder *builder = metaBuilderFromDict(reinterpret_cast<SbkObject*>(self)->ob_dict); + if (!builder) + builder = &(retrieveTypeUserData(self)->mo); - mo->update(); - return mo; + return builder->update(); } namespace { diff --git a/sources/pyside2/libpyside/signalmanager.h b/sources/pyside2/libpyside/signalmanager.h index 5948a7df1..229ddb91d 100644 --- a/sources/pyside2/libpyside/signalmanager.h +++ b/sources/pyside2/libpyside/signalmanager.h @@ -41,10 +41,12 @@ #define SIGNALMANAGER_H #include "pysidemacros.h" + #include <sbkpython.h> -#include <Qt> -#include <QStringList> -#include <QMetaMethod> + +#include <QtCore/QMetaMethod> + +QT_FORWARD_DECLARE_CLASS(QDataStream) namespace PySide { @@ -53,12 +55,19 @@ namespace PySide class PYSIDE_API PyObjectWrapper { public: + PyObjectWrapper(PyObjectWrapper&&) = delete; + PyObjectWrapper& operator=(PyObjectWrapper &&) = delete; + PyObjectWrapper(); - PyObjectWrapper(PyObject* me); + explicit PyObjectWrapper(PyObject* me); PyObjectWrapper(const PyObjectWrapper &other); + PyObjectWrapper& operator=(const PyObjectWrapper &other); + + void reset(PyObject *o); + ~PyObjectWrapper(); operator PyObject*() const; - PyObjectWrapper& operator=(const PyObjectWrapper &other); + private: PyObject* m_me; }; @@ -68,6 +77,7 @@ PYSIDE_API QDataStream &operator>>(QDataStream& in, PyObjectWrapper& myObj); class PYSIDE_API SignalManager { + Q_DISABLE_COPY(SignalManager) public: static SignalManager& instance(); @@ -84,7 +94,7 @@ public: static int registerMetaMethodGetIndex(QObject* source, const char* signature, QMetaMethod::MethodType type); // used to discovery metaobject - static const QMetaObject* retriveMetaObject(PyObject* self); + static const QMetaObject* retrieveMetaObject(PyObject* self); // Used to discovery if SignalManager was connected with object "destroyed()" signal. int countConnectionsWith(const QObject *object); @@ -95,24 +105,12 @@ public: // Utility function to call a python method usign args received in qt_metacall static int callPythonMetaMethod(const QMetaMethod& method, void** args, PyObject* obj, bool isShortCuit); - PYSIDE_DEPRECATED(QObject* globalReceiver()); - PYSIDE_DEPRECATED(void addGlobalSlot(const char* slot, PyObject* callback)); - PYSIDE_DEPRECATED(int addGlobalSlotGetIndex(const char* slot, PyObject* callback)); - - PYSIDE_DEPRECATED(void globalReceiverConnectNotify(QObject *sender, int slotIndex)); - PYSIDE_DEPRECATED(void globalReceiverDisconnectNotify(QObject *sender, int slotIndex)); - PYSIDE_DEPRECATED(bool hasConnectionWith(const QObject *object)); - private: struct SignalManagerPrivate; SignalManagerPrivate* m_d; SignalManager(); ~SignalManager(); - - // disable copy - SignalManager(const SignalManager&); - SignalManager operator=(const SignalManager&); }; } diff --git a/sources/pyside2/pyside_version.py b/sources/pyside2/pyside_version.py index 789812464..a883bab96 100644 --- a/sources/pyside2/pyside_version.py +++ b/sources/pyside2/pyside_version.py @@ -38,16 +38,10 @@ ############################################################################# major_version = "5" -minor_version = "11" -patch_version = "1" - -# For example: "a", "b", "rc" -# (which means "alpha", "beta", "release candidate"). -# An empty string means the generated package will be an official release. -pre_release_version_type = "a" - -# For example: "1", "2" (which means "beta1", "beta2", if type is "b"). -pre_release_version = "1" +minor_version = "12" +patch_version = "0" +pre_release_version_type = "a" # e.g. "a", "b", "rc". +pre_release_version = "1" # e.g "1", "2", (which means "beta1", "beta2", if type is "b") if __name__ == '__main__': # Used by CMake. diff --git a/sources/pyside2/tests/CMakeLists.txt b/sources/pyside2/tests/CMakeLists.txt index 2386950e9..bed2d7cc1 100644 --- a/sources/pyside2/tests/CMakeLists.txt +++ b/sources/pyside2/tests/CMakeLists.txt @@ -46,7 +46,7 @@ else() set_tests_properties(${TEST_NAME} PROPERTIES TIMEOUT ${CTEST_TESTING_TIMEOUT} WILL_FAIL ${EXPECT_TO_FAIL} - ENVIRONMENT "PYTHONPATH=${TEST_PYTHONPATH};${LIBRARY_PATH_VAR}=${TEST_LIBRARY_PATH};PYSIDE_DISABLE_INTERNAL_QT_CONF=1;QT_NO_GLIB=1") + ENVIRONMENT "PYTHONPATH=${TEST_PYTHONPATH};${LIBRARY_PATH_VAR}=${TEST_LIBRARY_PATH};PYSIDE_DISABLE_INTERNAL_QT_CONF=1;QT_NO_GLIB=1;QT_MAC_WANTS_LAYER=0") endmacro() if (NOT DISABLE_QtCore AND NOT DISABLE_QtGui AND NOT DISABLE_QtWidgets) diff --git a/sources/pyside2/tests/QtCore/CMakeLists.txt b/sources/pyside2/tests/QtCore/CMakeLists.txt index 649e796cc..d6d12f651 100644 --- a/sources/pyside2/tests/QtCore/CMakeLists.txt +++ b/sources/pyside2/tests/QtCore/CMakeLists.txt @@ -52,6 +52,7 @@ PYSIDE_TEST(qbytearray_concatenation_operator_test.py) PYSIDE_TEST(qbytearray_operator_iadd_test.py) PYSIDE_TEST(qbytearray_operator_test.py) PYSIDE_TEST(qbytearray_test.py) +PYSIDE_TEST(qcbor_test.py) PYSIDE_TEST(qcollator_test.py) PYSIDE_TEST(qcommandlineparser_test.py) PYSIDE_TEST(qcoreapplication_instance_test.py) diff --git a/sources/pyside2/tests/QtCore/qcbor_test.py b/sources/pyside2/tests/QtCore/qcbor_test.py new file mode 100644 index 000000000..2ac46673a --- /dev/null +++ b/sources/pyside2/tests/QtCore/qcbor_test.py @@ -0,0 +1,74 @@ +#!/usr/bin/python + +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the test suite of Qt for Python. +## +## $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 cases for QCbor''' + +import unittest + +from PySide2.QtCore import (QByteArray, QCborStreamReader, QCborStreamWriter, + QCborValue) + +class TestCbor(unittest.TestCase): + def testReader(self): + ba = QByteArray() + writer = QCborStreamWriter(ba) + writer.append(42) + del writer + self.assertTrue(not ba.isEmpty()) + reader = QCborStreamReader(ba) + self.assertTrue(reader.hasNext()) + value = reader.toInteger() + self.assertEqual(value, 42) + + def testReader(self): + ba = QByteArray() + writer = QCborStreamWriter(ba) + writer.append("hello") + del writer + self.assertTrue(not ba.isEmpty()) + reader = QCborStreamReader(ba) + self.assertTrue(reader.hasNext()) + if (reader.isByteArray()): # Python 2 + value = reader.readByteArray() + self.assertTrue(value) + self.assertEqual(value.data, "hello") + else: + self.assertTrue(reader.isString()) + value = reader.readString() + self.assertTrue(value) + self.assertEqual(value.data, "hello") + + def testValue(self): + value = QCborValue('hello') + self.assertTrue(value.isString()) + self.assertEqual(value.toString(), 'hello') + +if __name__ == '__main__': + unittest.main() diff --git a/sources/pyside2/tests/QtCore/qmetaobject_test.py b/sources/pyside2/tests/QtCore/qmetaobject_test.py index 12b5312a6..73ab81c7c 100644 --- a/sources/pyside2/tests/QtCore/qmetaobject_test.py +++ b/sources/pyside2/tests/QtCore/qmetaobject_test.py @@ -78,6 +78,12 @@ class qmetaobject_test(unittest.TestCase): #self.assertTrue(slot_index != signal_index) + # PYSIDE-784, plain Qt objects should not have intermediary + # metaObjects. + def test_PlainQObject(self): + timer = QTimer() + self.assertEqual(timer.metaObject().superClass().className(), + "QObject") if __name__ == '__main__': unittest.main() diff --git a/sources/pyside2/tests/QtGui/qmatrix_test.py b/sources/pyside2/tests/QtGui/qmatrix_test.py index cac8a7ab7..a917199c1 100644 --- a/sources/pyside2/tests/QtGui/qmatrix_test.py +++ b/sources/pyside2/tests/QtGui/qmatrix_test.py @@ -29,7 +29,7 @@ import unittest from PySide2.QtCore import QPoint -from PySide2.QtGui import QMatrix, QMatrix4x4 +from PySide2.QtGui import QMatrix, QMatrix2x2, QMatrix4x4 def qpointTimesQMatrix(point, matrix): @@ -49,6 +49,19 @@ class QMatrixTest(unittest.TestCase): point = QPoint(3, 3) self.assertRaises(TypeError, matrix.__mul__, point) + def testMatrix2x2(self): + matrix = QMatrix2x2([1.0, 2.0, 3.0, 4.0]) + + expectedTransposed = QMatrix2x2([1.0, 3.0, 2.0, 4.0]) + self.assertEqual(matrix.transposed(), expectedTransposed) + + expectedMultiplied = QMatrix2x2([2.0, 4.0, 6.0, 8.0]) + matrix *= 2.0 + self.assertEqual(matrix, expectedMultiplied) + + matrix.setToIdentity() + self.assertTrue(matrix.isIdentity()) + def testMatrix4x4(self): self.assertRaises(TypeError, QMatrix4x4, [0.0, 1.0, 2.0, 3.0]) self.assertRaises(TypeError, QMatrix4x4, [0.0, 1.0, 2.0, 'I', diff --git a/sources/pyside2/tests/QtNetwork/CMakeLists.txt b/sources/pyside2/tests/QtNetwork/CMakeLists.txt index c14c19fa9..57c5266c8 100644 --- a/sources/pyside2/tests/QtNetwork/CMakeLists.txt +++ b/sources/pyside2/tests/QtNetwork/CMakeLists.txt @@ -3,6 +3,7 @@ PYSIDE_TEST(bug_1084.py) PYSIDE_TEST(accessManager_test.py) PYSIDE_TEST(dnslookup_test.py) # Qt5: QHttp is gone PYSIDE_TEST(http_test.py) +PYSIDE_TEST(qpassworddigestor_test.py) PYSIDE_TEST(tcpserver_test.py) PYSIDE_TEST(udpsocket_test.py) PYSIDE_TEST(qipv6address_test.py) diff --git a/sources/pyside2/tests/QtNetwork/qpassworddigestor_test.py b/sources/pyside2/tests/QtNetwork/qpassworddigestor_test.py new file mode 100644 index 000000000..503ffecdc --- /dev/null +++ b/sources/pyside2/tests/QtNetwork/qpassworddigestor_test.py @@ -0,0 +1,45 @@ +#!/usr/bin/python + +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the test suite of Qt for Python. +## +## $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 cases for QPasswordDigestor''' + +import unittest + +from PySide2.QtCore import QByteArray, QCryptographicHash +from PySide2.QtNetwork import QPasswordDigestor + +class TestPasswordDigestor(unittest.TestCase): + def test(self): + b = QPasswordDigestor.deriveKeyPbkdf1(QCryptographicHash.Sha1, + b'test', b'saltnpep', 10, 20) + self.assertEqual(b.size(), 20) + +if __name__ == '__main__': + unittest.main() diff --git a/sources/pyside2/tests/QtWidgets/python_properties_test.py b/sources/pyside2/tests/QtWidgets/python_properties_test.py index f5bcf5eb8..f4e46f2bd 100644 --- a/sources/pyside2/tests/QtWidgets/python_properties_test.py +++ b/sources/pyside2/tests/QtWidgets/python_properties_test.py @@ -39,6 +39,8 @@ class Properties(unittest.TestCase): p = QtWidgets.QStyleOptionViewItem() self.assertTrue(isinstance(p.locale, QtCore.QLocale)) + # PSYIDE-304, can assign to a "const QWidget *" field + p.widget = None if __name__ == '__main__': unittest.main() diff --git a/sources/pyside2/tests/QtWidgets/qwidget_test.py b/sources/pyside2/tests/QtWidgets/qwidget_test.py index 1e8387d11..028751ba7 100644 --- a/sources/pyside2/tests/QtWidgets/qwidget_test.py +++ b/sources/pyside2/tests/QtWidgets/qwidget_test.py @@ -26,6 +26,7 @@ ## ############################################################################# +import sys import unittest from PySide2.QtWidgets import QWidget, QMainWindow @@ -35,6 +36,17 @@ class QWidgetInherit(QMainWindow): def __init__(self): QWidget.__init__(self) +class NativeEventTestWidget(QWidget): + + nativeEventCount = 0 + + def __init__(self): + QWidget.__init__(self) + + def nativeEvent(self, eventType, message): + self.nativeEventCount = self.nativeEventCount + 1 + return [False, 0] + class QWidgetTest(UsesQApplication): def testInheritance(self): @@ -44,12 +56,19 @@ class QWidgetVisible(UsesQApplication): def testBasic(self): # Also related to bug #244, on existence of setVisible''' - widget = QWidget() + widget = NativeEventTestWidget() self.assertTrue(not widget.isVisible()) widget.setVisible(True) self.assertTrue(widget.isVisible()) self.assertTrue(widget.winId() is not 0) - + # skip this test on macOS since no native events are received + if sys.platform == 'darwin': + return + for i in range(10): + if widget.nativeEventCount > 0: + break + self.app.processEvents() + self.assertTrue(widget.nativeEventCount > 0) if __name__ == '__main__': unittest.main() diff --git a/sources/pyside2/tests/pysidetest/CMakeLists.txt b/sources/pyside2/tests/pysidetest/CMakeLists.txt index c6d3bb13b..119553fad 100644 --- a/sources/pyside2/tests/pysidetest/CMakeLists.txt +++ b/sources/pyside2/tests/pysidetest/CMakeLists.txt @@ -3,9 +3,6 @@ project(testbinding) cmake_minimum_required(VERSION 3.1) -# On Windows, don't link to qtmain.lib for executables automatically. -cmake_policy(SET CMP0020 OLD) - set(QT_USE_QTCORE 1) # no more supported: include(${QT_USE_FILE}) add_definitions(${Qt5Core_DEFINITIONS}) @@ -69,7 +66,9 @@ make_path(testbinding_include_dirs ${pyside2_BINARY_DIR} make_path(testbinding_typesystem_path ${pyside2_SOURCE_DIR} ${pyside2_BINARY_DIR}) -add_custom_command(OUTPUT ${testbinding_SRC} +add_custom_command( +OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/mjb_rejected_classes.log" +BYPRODUCTS ${testbinding_SRC} COMMAND ${SHIBOKEN_BINARY} ${GENERATOR_EXTRA_FLAGS} ${CMAKE_CURRENT_SOURCE_DIR}/pysidetest_global.h --include-paths=${testbinding_include_dirs} @@ -119,6 +118,7 @@ target_link_libraries(testbinding ${SBK_PYTHON_LIBRARIES}) add_dependencies(testbinding pyside2 QtCore QtGui QtWidgets pysidetest) +create_generator_target(testbinding) PYSIDE_TEST(decoratedslot_test.py) # Will always crash when built against Qt 5.6, no point in running it. diff --git a/sources/pyside2/tests/pysidetest/new_inherited_functions_test.py b/sources/pyside2/tests/pysidetest/new_inherited_functions_test.py index 960f675ea..54b81acae 100644 --- a/sources/pyside2/tests/pysidetest/new_inherited_functions_test.py +++ b/sources/pyside2/tests/pysidetest/new_inherited_functions_test.py @@ -32,17 +32,16 @@ import sys import os import unittest -import PySide2.QtCore +from PySide2 import * +for modname, mod in sys.modules.items(): + # Python 2 leaves "None" in the dict. + if modname.startswith("PySide2.") and mod is not None: + print("importing", modname) + exec("import " + modname) # This test tests the existence and callability of the newly existing functions, # after the inheritance was made complete in the course of PYSIDE-331. -def warn_essential(modname): - print(80 * "*") - print("*** Warning: '{}' is an essential module! Are you sure to skip it?" - .format(modname)) - print(80 * "*") - new_functions = """ PySide2.QtCore.QAbstractItemModel().parent() PySide2.QtCore.QAbstractListModel().parent() @@ -52,85 +51,67 @@ new_functions = """ PySide2.QtCore.QSortFilterProxyModel().parent() PySide2.QtCore.QTemporaryFile(tfarg).open(openMode) """ -try: - modname = "PySide2.QtGui" - exec("import " + modname) - new_functions += """ - PySide2.QtGui.QBitmap().transformed(qMatrix,transformationMode) - PySide2.QtGui.QStandardItemModel().insertColumn(int,qModelIndex) - PySide2.QtGui.QStandardItemModel().parent() - # PySide2.QtGui.QTextList(qTextDocument).setFormat(qTextFormat) # Segmentation fault: 11 - # PySide2.QtGui.QTextTable(qTextDocument).setFormat(qTextFormat) # Segmentation fault: 11 - """ -except ImportError: - warn_essential(modname) -try: - modname = "PySide2.QtWidgets" - exec("import " + modname) - new_functions += """ - PySide2.QtWidgets.QAbstractItemView().update() - PySide2.QtWidgets.QApplication.palette() - PySide2.QtWidgets.QApplication.setFont(qFont) - PySide2.QtWidgets.QApplication.setPalette(qPalette) - PySide2.QtWidgets.QBoxLayout(direction).addWidget(qWidget) - PySide2.QtWidgets.QColorDialog().open() - PySide2.QtWidgets.QDirModel().index(int,int,qModelIndex) - PySide2.QtWidgets.QDirModel().parent() - PySide2.QtWidgets.QFileDialog().open() - PySide2.QtWidgets.QFileSystemModel().index(int,int,qModelIndex) - PySide2.QtWidgets.QFileSystemModel().parent() - PySide2.QtWidgets.QFontDialog().open() - PySide2.QtWidgets.QGestureEvent([]).accept() - PySide2.QtWidgets.QGestureEvent([]).ignore() - PySide2.QtWidgets.QGestureEvent([]).isAccepted() - PySide2.QtWidgets.QGestureEvent([]).setAccepted(bool) - # PySide2.QtWidgets.QGraphicsView().render(qPaintDevice,qPoint,qRegion,renderFlags) # QPaintDevice: NotImplementedError - PySide2.QtWidgets.QGridLayout().addWidget(qWidget) - PySide2.QtWidgets.QHeaderView(orientation).initStyleOption(qStyleOptionFrame) - PySide2.QtWidgets.QInputDialog().open() - PySide2.QtWidgets.QLineEdit().addAction(qAction) - PySide2.QtWidgets.QListWidget().closePersistentEditor(qModelIndex) - PySide2.QtWidgets.QListWidget().openPersistentEditor(qModelIndex) - PySide2.QtWidgets.QMessageBox().open() - PySide2.QtWidgets.QPlainTextEdit.find(quintptr) - PySide2.QtWidgets.QProgressDialog().open() - PySide2.QtWidgets.QStackedLayout().widget() - # PySide2.QtWidgets.QStylePainter().begin(qPaintDevice) # QPaintDevice: NotImplementedError - PySide2.QtWidgets.QTableWidget().closePersistentEditor(qModelIndex) - PySide2.QtWidgets.QTableWidget().openPersistentEditor(qModelIndex) - PySide2.QtWidgets.QTextEdit.find(quintptr) - PySide2.QtWidgets.QTreeWidget().closePersistentEditor(qModelIndex) - PySide2.QtWidgets.QTreeWidget().openPersistentEditor(qModelIndex) - """ -except ImportError: - warn_essential(modname) -try: - modname = "PySide2.QtPrintSupport" - exec("import " + modname) - new_functions += """ - # PySide2.QtPrintSupport.QPageSetupDialog().open() # Segmentation fault: 11 - # PySide2.QtPrintSupport.QPrintDialog().open() # opens the dialog, but works - PySide2.QtPrintSupport.QPrintDialog().printer() - PySide2.QtPrintSupport.QPrintPreviewDialog().open() # note: this prints something, but really shouldn't ;-) - """ -except ImportError: - warn_essential(modname) -try: - import PySide2.QtHelp - new_functions += """ - PySide2.QtHelp.QHelpContentModel().parent() - # PySide2.QtHelp.QHelpIndexModel().createIndex(int,int,quintptr) # returned NULL without setting an error - # PySide2.QtHelp.QHelpIndexModel().createIndex(int,int,object()) # returned NULL without setting an error - """ -except ImportError: - pass -try: - import PySide2.QtQuick - new_functions += """ - PySide2.QtQuick.QQuickPaintedItem().update() - """ -except ImportError: - pass + +new_functions += """ + PySide2.QtGui.QBitmap().transformed(qMatrix,transformationMode) + PySide2.QtGui.QStandardItemModel().insertColumn(int,qModelIndex) + PySide2.QtGui.QStandardItemModel().parent() + # PySide2.QtGui.QTextList(qTextDocument).setFormat(qTextFormat) # Segmentation fault: 11 + # PySide2.QtGui.QTextTable(qTextDocument).setFormat(qTextFormat) # Segmentation fault: 11 +""" if "PySide2.QtGui" in sys.modules else "" + +new_functions += """ + PySide2.QtWidgets.QAbstractItemView().update() + PySide2.QtWidgets.QApplication.palette() + PySide2.QtWidgets.QApplication.setFont(qFont) + PySide2.QtWidgets.QApplication.setPalette(qPalette) + PySide2.QtWidgets.QBoxLayout(direction).addWidget(qWidget) + PySide2.QtWidgets.QColorDialog().open() + PySide2.QtWidgets.QDirModel().index(int,int,qModelIndex) + PySide2.QtWidgets.QDirModel().parent() + PySide2.QtWidgets.QFileDialog().open() + PySide2.QtWidgets.QFileSystemModel().index(int,int,qModelIndex) + PySide2.QtWidgets.QFileSystemModel().parent() + PySide2.QtWidgets.QFontDialog().open() + PySide2.QtWidgets.QGestureEvent([]).accept() + PySide2.QtWidgets.QGestureEvent([]).ignore() + PySide2.QtWidgets.QGestureEvent([]).isAccepted() + PySide2.QtWidgets.QGestureEvent([]).setAccepted(bool) + # PySide2.QtWidgets.QGraphicsView().render(qPaintDevice,qPoint,qRegion,renderFlags) # QPaintDevice: NotImplementedError + PySide2.QtWidgets.QGridLayout().addWidget(qWidget) + PySide2.QtWidgets.QHeaderView(orientation).initStyleOption(qStyleOptionFrame) + PySide2.QtWidgets.QInputDialog().open() + PySide2.QtWidgets.QLineEdit().addAction(qAction) + PySide2.QtWidgets.QListWidget().closePersistentEditor(qModelIndex) + PySide2.QtWidgets.QListWidget().openPersistentEditor(qModelIndex) + PySide2.QtWidgets.QMessageBox().open() + PySide2.QtWidgets.QPlainTextEdit.find(quintptr) + PySide2.QtWidgets.QProgressDialog().open() + PySide2.QtWidgets.QStackedLayout().widget() + # PySide2.QtWidgets.QStylePainter().begin(qPaintDevice) # QPaintDevice: NotImplementedError + PySide2.QtWidgets.QTableWidget().closePersistentEditor(qModelIndex) + PySide2.QtWidgets.QTableWidget().openPersistentEditor(qModelIndex) + PySide2.QtWidgets.QTextEdit.find(quintptr) + PySide2.QtWidgets.QTreeWidget().closePersistentEditor(qModelIndex) + PySide2.QtWidgets.QTreeWidget().openPersistentEditor(qModelIndex) +""" if "PySide2.QtWidgets" in sys.modules else "" + +new_functions += """ + # PySide2.QtPrintSupport.QPageSetupDialog().open() # Segmentation fault: 11 + # PySide2.QtPrintSupport.QPrintDialog().open() # opens the dialog, but works + PySide2.QtPrintSupport.QPrintDialog().printer() + PySide2.QtPrintSupport.QPrintPreviewDialog().open() # note: this prints something, but really shouldn't ;-) +""" if "PySide2.QtPrintSupport" in sys.modules else "" + +new_functions += """ + PySide2.QtHelp.QHelpContentModel().parent() + # PySide2.QtHelp.QHelpIndexModel().createIndex(int,int,quintptr) # returned NULL without setting an error + # PySide2.QtHelp.QHelpIndexModel().createIndex(int,int,object()) # returned NULL without setting an error +""" if "PySide2.QtHelp" in sys.modules else "" + +new_functions += """ + PySide2.QtQuick.QQuickPaintedItem().update() +""" if "PySide2.QtQuick" in sys.modules else "" class MainTest(unittest.TestCase): diff --git a/sources/pyside2/tests/registry/init_platform.py b/sources/pyside2/tests/registry/init_platform.py index 03d0ed133..22875a63e 100644 --- a/sources/pyside2/tests/registry/init_platform.py +++ b/sources/pyside2/tests/registry/init_platform.py @@ -55,9 +55,10 @@ from textwrap import dedent all_modules = list("PySide2." + x for x in PySide2.__all__) -from PySide2.support.signature import inspect from PySide2.QtCore import __version__ +from PySide2.support.signature.lib.enum_sig import SimplifyingEnumerator + is_py3 = sys.version_info[0] == 3 is_ci = os.environ.get("QTEST_ENVIRONMENT", "") == "ci" # Python2 legacy: Correct 'linux2' to 'linux', recommended way. @@ -114,7 +115,7 @@ class Formatter(object): Formatter is formatting the signature listing of an enumerator. It is written as context managers in order to avoid many callbacks. - The division in formatter and enumerator is done to keep the + The separation in formatter and enumerator is done to keep the unrelated tasks of enumeration and formatting apart. """ def __init__(self, outfile): @@ -134,7 +135,7 @@ class Formatter(object): self.print(" })") @contextmanager - def klass(self, class_name): + def klass(self, class_name, class_str): self.class_name = class_name self.print() self.print(" # class {}.{}:".format(self.mod_name, class_name)) @@ -152,89 +153,6 @@ class Formatter(object): yield key -class ExactEnumerator(object): - """ - ExactEnumerator enumerates all signatures in a module as they are. - - This class is used for generating complete listings of all signatures. - An appropriate formatter should be supplied, if printable output - is desired. - """ - def __init__(self, formatter, result_type=dict): - self.fmt = formatter - self.result_type = result_type - - def module(self, mod_name): - __import__(mod_name) - with self.fmt.module(mod_name): - module = sys.modules[mod_name] - members = inspect.getmembers(module, inspect.isclass) - ret = self.result_type() - for class_name, klass in members: - ret.update(self.klass(class_name, klass)) - return ret - - def klass(self, class_name, klass): - with self.fmt.klass(class_name): - ret = self.function("__init__", klass) - # class_members = inspect.getmembers(klass) - # gives us also the inherited things. - class_members = sorted(list(klass.__dict__.items())) - for func_name, func in class_members: - ret.update(self.function(func_name, func)) - return ret - - def function(self, func_name, func): - ret = self.result_type() - signature = getattr(func, '__signature__', None) - if signature is not None: - with self.fmt.function(func_name, signature) as key: - ret[key] = signature - return ret - - -def simplify(signature): - if isinstance(signature, list): - # remove duplicates which still sometimes occour: - ret = set(simplify(sig) for sig in signature) - return sorted(ret) if len(ret) > 1 else list(ret)[0] - ret = [] - for pv in signature.parameters.values(): - txt = str(pv) - if txt == "self": - continue - txt = txt[txt.index(":") + 1:] - if "=" in txt: - txt = txt[:txt.index("=")] - quote = txt[0] - if quote in ("'", '"') and txt[-1] == quote: - txt = txt[1:-1] - ret.append(txt) - return tuple(ret) - - -class SimplifyingEnumerator(ExactEnumerator): - """ - SimplifyingEnumerator enumerates all signatures in a module filtered. - - There are no default values, no variable - names and no self parameter. Only types are present after simplification. - The functions 'next' resp. '__next__' are removed - to make the output identical for Python 2 and 3. - An appropriate formatter should be supplied, if printable output - is desired. - """ - - def function(self, func_name, func): - ret = self.result_type() - signature = getattr(func, '__signature__', None) - sig = simplify(signature) if signature is not None else None - if sig is not None and func_name not in ("next", "__next__"): - with self.fmt.function(func_name, sig) as key: - ret[key] = sig - return ret - - def enum_all(): fmt = Formatter(None) enu = SimplifyingEnumerator(fmt) diff --git a/sources/shiboken2/ApiExtractor/CMakeLists.txt b/sources/shiboken2/ApiExtractor/CMakeLists.txt index b67a352f0..b4fd1cb99 100644 --- a/sources/shiboken2/ApiExtractor/CMakeLists.txt +++ b/sources/shiboken2/ApiExtractor/CMakeLists.txt @@ -37,6 +37,7 @@ abstractmetabuilder.cpp abstractmetalang.cpp fileout.cpp graph.cpp +messages.cpp reporthandler.cpp typeparser.cpp typesystem.cpp diff --git a/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp b/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp index 84c116708..9653831cc 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp +++ b/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp @@ -27,6 +27,7 @@ ****************************************************************************/ #include "abstractmetabuilder_p.h" +#include "messages.h" #include "reporthandler.h" #include "typedatabase.h" @@ -159,38 +160,18 @@ AbstractMetaEnumList AbstractMetaBuilder::globalEnums() const return d->m_globalEnums; } -static QString msgNoFunctionForModification(const QString &signature, - const QString &originalSignature, - const QString &className, - const QStringList &possibleSignatures, - const AbstractMetaFunctionList &allFunctions) -{ - QString result; - QTextStream str(&result); - str << "signature '" << signature << '\''; - if (!originalSignature.isEmpty() && originalSignature != signature) - str << " (specified as '" << originalSignature << "')"; - str << " for function modification in '" - << className << "' not found."; - if (possibleSignatures.isEmpty()) { - str << " No candidates were found. Member functions: "; - for (int f = 0, size = allFunctions.size(); f < size; ++f) { - if (f) - str << ", "; - str << allFunctions.at(f)->minimalSignature(); - } - } else { - str << " Possible candidates: " << possibleSignatures.join(QLatin1String(", ")); - } - return result; +AbstractMetaEnum *AbstractMetaBuilder::findEnum(const TypeEntry *typeEntry) const +{ + if (typeEntry && typeEntry->isFlags()) + typeEntry = static_cast<const FlagsTypeEntry*>(typeEntry)->originator(); + return d->m_enums.value(typeEntry); } void AbstractMetaBuilderPrivate::checkFunctionModifications() { - TypeDatabase *types = TypeDatabase::instance(); - const SingleTypeEntryHash entryHash = types->entries(); + const auto &entries = TypeDatabase::instance()->entries(); - for (SingleTypeEntryHash::const_iterator it = entryHash.cbegin(), end = entryHash.cend(); it != end; ++it) { + for (auto it = entries.cbegin(), end = entries.cend(); it != end; ++it) { const TypeEntry *entry = it.value(); if (!entry) continue; @@ -241,7 +222,7 @@ void AbstractMetaBuilderPrivate::checkFunctionModifications() } } -AbstractMetaClass *AbstractMetaBuilderPrivate::argumentToClass(ArgumentModelItem argument) +AbstractMetaClass *AbstractMetaBuilderPrivate::argumentToClass(const ArgumentModelItem &argument) { AbstractMetaClass* returned = 0; AbstractMetaType *type = translateType(argument->type()); @@ -256,7 +237,7 @@ AbstractMetaClass *AbstractMetaBuilderPrivate::argumentToClass(ArgumentModelItem /** * Checks the argument of a hash function and flags the type if it is a complex type */ -void AbstractMetaBuilderPrivate::registerHashFunction(FunctionModelItem function_item) +void AbstractMetaBuilderPrivate::registerHashFunction(const FunctionModelItem &function_item) { ArgumentList arguments = function_item->arguments(); if (arguments.size() == 1) { @@ -269,12 +250,12 @@ void AbstractMetaBuilderPrivate::registerHashFunction(FunctionModelItem function * Check if a class has a debug stream operator that can be used as toString */ -void AbstractMetaBuilderPrivate::registerToStringCapability(FunctionModelItem function_item) +void AbstractMetaBuilderPrivate::registerToStringCapability(const FunctionModelItem &function_item) { ArgumentList arguments = function_item->arguments(); if (arguments.size() == 2) { if (arguments.at(0)->type().toString() == QLatin1String("QDebug")) { - ArgumentModelItem arg = arguments.at(1); + const ArgumentModelItem &arg = arguments.at(1); if (AbstractMetaClass *cls = argumentToClass(arg)) { if (arg->type().indirections() < 2) cls->setToStringCapability(true); @@ -283,7 +264,7 @@ void AbstractMetaBuilderPrivate::registerToStringCapability(FunctionModelItem fu } } -void AbstractMetaBuilderPrivate::traverseOperatorFunction(FunctionModelItem item) +void AbstractMetaBuilderPrivate::traverseOperatorFunction(const FunctionModelItem &item) { if (item->accessPolicy() != CodeModel::Public) return; @@ -348,7 +329,7 @@ void AbstractMetaBuilderPrivate::traverseOperatorFunction(FunctionModelItem item setupFunctionDefaults(metaFunction, baseoperandClass); baseoperandClass->addFunction(metaFunction); Q_ASSERT(!metaFunction->wasPrivate()); - } else if (metaFunction) { + } else { delete metaFunction; } @@ -356,7 +337,7 @@ void AbstractMetaBuilderPrivate::traverseOperatorFunction(FunctionModelItem item } } -void AbstractMetaBuilderPrivate::traverseStreamOperator(FunctionModelItem item) +void AbstractMetaBuilderPrivate::traverseStreamOperator(const FunctionModelItem &item) { ArgumentList arguments = item->arguments(); if (arguments.size() == 2 && item->accessPolicy() == CodeModel::Public) { @@ -404,7 +385,7 @@ void AbstractMetaBuilderPrivate::traverseStreamOperator(FunctionModelItem item) funcClass->typeEntry()->addExtraInclude(streamClass->typeEntry()->include()); m_currentClass = oldCurrentClass; - } else if (streamFunction) { + } else { delete streamFunction; } @@ -422,7 +403,7 @@ void AbstractMetaBuilderPrivate::fixQObjectForScope(const FileModelItem &dom, TypeEntry* entry = types->findType(qualifiedName); if (entry) { if (isQObject(dom, qualifiedName) && entry->isComplex()) - ((ComplexTypeEntry*) entry)->setQObject(true); + static_cast<ComplexTypeEntry *>(entry)->setQObject(true); } } @@ -475,19 +456,18 @@ void AbstractMetaBuilderPrivate::traverseDom(const FileModelItem &dom) const ClassList &typeValues = dom->classes(); ReportHandler::setProgressReference(typeValues); for (const ClassModelItem &item : typeValues) { - ReportHandler::progress(QLatin1String("Generating class model...")); - AbstractMetaClass *cls = traverseClass(dom, item); - if (!cls) - continue; - - addAbstractMetaClass(cls); + ReportHandler::progress(QStringLiteral("Generating class model (%1)...") + .arg(typeValues.size())); + if (AbstractMetaClass *cls = traverseClass(dom, item)) + addAbstractMetaClass(cls); } // We need to know all global enums const EnumList &enums = dom->enums(); ReportHandler::setProgressReference(enums); for (const EnumModelItem &item : enums) { - ReportHandler::progress(QLatin1String("Generating enum model...")); + ReportHandler::progress(QStringLiteral("Generating enum model (%1)...") + .arg(enums.size())); AbstractMetaEnum *metaEnum = traverseEnum(item, 0, QSet<QString>()); if (metaEnum) { if (metaEnum->typeEntry()->generateCode()) @@ -498,7 +478,8 @@ void AbstractMetaBuilderPrivate::traverseDom(const FileModelItem &dom) const QSet<NamespaceModelItem> &namespaceTypeValues = dom->uniqueNamespaces(); ReportHandler::setProgressReference(namespaceTypeValues); for (const NamespaceModelItem &item : namespaceTypeValues) { - ReportHandler::progress(QLatin1String("Generating namespace model...")); + ReportHandler::progress(QStringLiteral("Generating namespace model (%1)...") + .arg(namespaceTypeValues.size())); AbstractMetaClass *metaClass = traverseNamespace(dom, item); if (metaClass) m_metaClasses << metaClass; @@ -509,11 +490,14 @@ void AbstractMetaBuilderPrivate::traverseDom(const FileModelItem &dom) const TypeDefList typeDefs = dom->typeDefs(); ReportHandler::setProgressReference(typeDefs); for (const TypeDefModelItem &typeDef : typeDefs) { - ReportHandler::progress(QLatin1String("Resolving typedefs...")); - AbstractMetaClass* cls = traverseTypeDef(dom, typeDef); - addAbstractMetaClass(cls); + ReportHandler::progress(QStringLiteral("Resolving typedefs (%1)...") + .arg(typeDefs.size())); + if (AbstractMetaClass *cls = traverseTypeDef(dom, typeDef)) + addAbstractMetaClass(cls); } + traverseTypesystemTypedefs(); + for (const ClassModelItem &item : typeValues) traverseClassMembers(item); @@ -580,13 +564,12 @@ void AbstractMetaBuilderPrivate::traverseDom(const FileModelItem &dom) if (cls->isAbstract() && !cls->isInterface()) cls->typeEntry()->setLookupName(cls->typeEntry()->targetLangName() + QLatin1String("$ConcreteWrapper")); } - const TypeEntryHash allEntries = types->allEntries(); - ReportHandler::progress(QLatin1String("Detecting inconsistencies in typesystem...")); - for (TypeEntryHash::const_iterator it = allEntries.cbegin(), end = allEntries.cend(); it != end; ++it) { - for (TypeEntry *entry : it.value()) { - if (entry->isPrimitive()) - continue; - + const auto &allEntries = types->entries(); + ReportHandler::progress(QStringLiteral("Detecting inconsistencies in typesystem (%1)...") + .arg(allEntries.size())); + for (auto it = allEntries.cbegin(), end = allEntries.cend(); it != end; ++it) { + TypeEntry *entry = it.value(); + if (!entry->isPrimitive()) { if ((entry->isValue() || entry->isObject()) && !entry->isString() && !entry->isChar() @@ -616,20 +599,13 @@ void AbstractMetaBuilderPrivate::traverseDom(const FileModelItem &dom) } } } else if (entry->isEnum() && (entry->generateCode() & TypeEntry::GenerateTargetLang)) { - const QString name = ((EnumTypeEntry*) entry)->targetLangQualifier(); + const QString name = static_cast<const EnumTypeEntry *>(entry)->targetLangQualifier(); AbstractMetaClass *cls = AbstractMetaClass::findClass(m_metaClasses, name); - bool enumFound = false; - if (cls) { - enumFound = cls->findEnum(entry->targetLangName()); - } else { // Global enum - for (AbstractMetaEnum *metaEnum : qAsConst(m_enums)) { - if (metaEnum->typeEntry() == entry) { - enumFound = true; - break; - } - } - } + const bool enumFound = cls + ? cls->findEnum(entry->targetLangName()) != nullptr + : m_enums.contains(entry); + if (!enumFound) { entry->setCodeGeneration(TypeEntry::GenerateNothing); qCWarning(lcShiboken).noquote().nospace() @@ -727,6 +703,15 @@ void AbstractMetaBuilderPrivate::traverseDom(const FileModelItem &dom) std::puts(""); } +static bool metaEnumLessThan(const AbstractMetaEnum *e1, const AbstractMetaEnum *e2) +{ return e1->fullName() < e2->fullName(); } + +static bool metaClassLessThan(const AbstractMetaClass *c1, const AbstractMetaClass *c2) +{ return c1->fullName() < c2->fullName(); } + +static bool metaFunctionLessThan(const AbstractMetaFunction *f1, const AbstractMetaFunction *f2) +{ return f1->name() < f2->name(); } + bool AbstractMetaBuilder::build(const QByteArrayList &arguments, LanguageLevel level, unsigned clangFlags) @@ -737,6 +722,14 @@ bool AbstractMetaBuilder::build(const QByteArrayList &arguments, if (ReportHandler::isDebug(ReportHandler::MediumDebug)) qCDebug(lcShiboken) << dom.data(); d->traverseDom(dom); + + // Ensure that indexes are in alphabetical order, roughly + std::sort(d->m_globalEnums.begin(), d->m_globalEnums.end(), metaEnumLessThan); + std::sort(d->m_metaClasses.begin(), d->m_metaClasses.end(), metaClassLessThan); + std::sort(d->m_templates.begin(), d->m_templates.end(), metaClassLessThan); + std::sort(d->m_smartPointers.begin(), d->m_smartPointers.end(), metaClassLessThan); + std::sort(d->m_globalFunctions.begin(), d->m_globalFunctions.end(), metaFunctionLessThan); + return true; } @@ -749,9 +742,6 @@ void AbstractMetaBuilder::setLogDirectory(const QString& logDir) void AbstractMetaBuilderPrivate::addAbstractMetaClass(AbstractMetaClass *cls) { - if (!cls) - return; - cls->setOriginalAttributes(cls->attributes()); if (cls->typeEntry()->isContainer()) { m_templates << cls; @@ -848,7 +838,7 @@ AbstractMetaClass *AbstractMetaBuilderPrivate::traverseNamespace(const FileModel return metaClass; } -AbstractMetaEnum *AbstractMetaBuilderPrivate::traverseEnum(EnumModelItem enumItem, +AbstractMetaEnum *AbstractMetaBuilderPrivate::traverseEnum(const EnumModelItem &enumItem, AbstractMetaClass *enclosing, const QSet<QString> &enumsDeclarations) { @@ -857,7 +847,7 @@ AbstractMetaEnum *AbstractMetaBuilderPrivate::traverseEnum(EnumModelItem enumIte TypeEntry* typeEntry = 0; if (enumItem->accessPolicy() == CodeModel::Private) { QStringList names = enumItem->qualifiedName(); - QString enumName = names.constLast(); + const QString &enumName = names.constLast(); QString nspace; if (names.size() > 1) nspace = QStringList(names.mid(0, names.size() - 1)).join(colonColon()); @@ -892,15 +882,23 @@ AbstractMetaEnum *AbstractMetaBuilderPrivate::traverseEnum(EnumModelItem enumIte return 0; } - if ((!typeEntry || !typeEntry->isEnum())) { - if (!m_currentClass || - (m_currentClass->typeEntry()->codeGeneration() & TypeEntry::GenerateTargetLang)) { - qCWarning(lcShiboken).noquote().nospace() - << QStringLiteral("enum '%1' does not have a type entry or is not an enum") - .arg(qualifiedName); + const bool rejectionWarning = !m_currentClass + || (m_currentClass->typeEntry()->codeGeneration() & TypeEntry::GenerateTargetLang); + + if (!typeEntry) { + if (rejectionWarning) + qCWarning(lcShiboken, "%s", qPrintable(msgNoEnumTypeEntry(enumItem, className))); + m_rejectedEnums.insert(qualifiedName, AbstractMetaBuilder::NotInTypeSystem); + return nullptr; + } + + if (!typeEntry->isEnum()) { + if (rejectionWarning) { + qCWarning(lcShiboken, "%s", + qPrintable(msgNoEnumTypeConflict(enumItem, className, typeEntry))); } m_rejectedEnums.insert(qualifiedName, AbstractMetaBuilder::NotInTypeSystem); - return 0; + return nullptr; } AbstractMetaEnum *metaEnum = new AbstractMetaEnum; @@ -946,15 +944,9 @@ AbstractMetaEnum *AbstractMetaBuilderPrivate::traverseEnum(EnumModelItem enumIte qCDebug(lcShiboken) << " - " << metaEnumValue->name() << " = " << metaEnumValue->value() << " = " << metaEnumValue->value(); } - - // Add into global register... - if (enclosing) - m_enumValues[enclosing->name() + colonColon() + metaEnumValue->name()] = metaEnumValue; - else - m_enumValues[metaEnumValue->name()] = metaEnumValue; } - m_enums << metaEnum; + m_enums.insert(typeEntry, metaEnum); if (!metaEnum->typeEntry()->include().isValid()) setInclude(metaEnum->typeEntry(), enumItem->fileName()); @@ -962,6 +954,15 @@ AbstractMetaEnum *AbstractMetaBuilderPrivate::traverseEnum(EnumModelItem enumIte metaEnum->setOriginalAttributes(metaEnum->attributes()); // Register all enum values on Type database + QString prefix; + if (enclosing) { + prefix += enclosing->typeEntry()->qualifiedCppName(); + prefix += colonColon(); + } + if (enumItem->enumKind() == EnumClass) { + prefix += enumItem->name(); + prefix += colonColon(); + } const EnumeratorList &enumerators = enumItem->enumerators(); for (const EnumeratorModelItem &e : enumerators) { QString name; @@ -969,11 +970,12 @@ AbstractMetaEnum *AbstractMetaBuilderPrivate::traverseEnum(EnumModelItem enumIte name += enclosing->name(); name += colonColon(); } - name += e->name(); EnumValueTypeEntry *enumValue = - new EnumValueTypeEntry(name, e->stringValue(), + new EnumValueTypeEntry(prefix + e->name(), e->stringValue(), enumTypeEntry, enumTypeEntry->version()); TypeDatabase::instance()->addType(enumValue); + if (e->value().isNullValue()) + enumTypeEntry->setNullValue(enumValue); } return metaEnum; @@ -1014,7 +1016,7 @@ AbstractMetaClass* AbstractMetaBuilderPrivate::traverseTypeDef(const FileModelIt AbstractMetaClass *metaClass = new AbstractMetaClass; metaClass->setTypeDef(true); metaClass->setTypeEntry(type); - metaClass->setBaseClassNames(QStringList() << typeDef->type().qualifiedName().join(colonColon())); + metaClass->setBaseClassNames(QStringList(typeDef->type().toString())); *metaClass += AbstractMetaAttributes::Public; // Set the default include file name @@ -1026,6 +1028,22 @@ AbstractMetaClass* AbstractMetaBuilderPrivate::traverseTypeDef(const FileModelIt return metaClass; } +// Add the typedef'ed classes +void AbstractMetaBuilderPrivate::traverseTypesystemTypedefs() +{ + const auto &entries = TypeDatabase::instance()->typedefEntries(); + for (auto it = entries.begin(), end = entries.end(); it != end; ++it) { + TypedefEntry *te = it.value(); + AbstractMetaClass *metaClass = new AbstractMetaClass; + metaClass->setTypeDef(true); + metaClass->setTypeEntry(te->target()); + metaClass->setBaseClassNames(QStringList(te->sourceType())); + *metaClass += AbstractMetaAttributes::Public; + fillAddedFunctions(metaClass); + addAbstractMetaClass(metaClass); + } +} + AbstractMetaClass *AbstractMetaBuilderPrivate::traverseClass(const FileModelItem &dom, const ClassModelItem &classItem) { @@ -1204,18 +1222,18 @@ void AbstractMetaBuilderPrivate::traverseNamespaceMembers(NamespaceModelItem ite m_currentClass = oldCurrentClass; } -static inline QString fieldSignatureWithType(VariableModelItem field) +static inline QString fieldSignatureWithType(const VariableModelItem &field) { return field->name() + QStringLiteral(" -> ") + field->type().toString(); } static inline QString qualifiedFieldSignatureWithType(const QString &className, - VariableModelItem field) + const VariableModelItem &field) { return className + colonColon() + fieldSignatureWithType(field); } -AbstractMetaField *AbstractMetaBuilderPrivate::traverseField(VariableModelItem field, +AbstractMetaField *AbstractMetaBuilderPrivate::traverseField(const VariableModelItem &field, const AbstractMetaClass *cls) { QString fieldName = field->name(); @@ -1272,7 +1290,7 @@ AbstractMetaField *AbstractMetaBuilderPrivate::traverseField(VariableModelItem f return metaField; } -void AbstractMetaBuilderPrivate::traverseFields(ScopeModelItem scope_item, +void AbstractMetaBuilderPrivate::traverseFields(const ScopeModelItem &scope_item, AbstractMetaClass *metaClass) { const VariableList &variables = scope_item->variables(); @@ -1327,14 +1345,8 @@ void AbstractMetaBuilderPrivate::fixReturnTypeOfConversionOperator(AbstractMetaF static bool _compareAbstractMetaTypes(const AbstractMetaType* type, const AbstractMetaType* other) { - if (!type && !other) - return true; - if (!type || !other) - return false; - return type->typeEntry() == other->typeEntry() - && type->isConstant() == other->isConstant() - && type->referenceType() == other->referenceType() - && type->indirections() == other->indirections(); + return (type != nullptr) == (other != nullptr) + && (type == nullptr || *type == *other); } static bool _compareAbstractMetaFunctions(const AbstractMetaFunction* func, const AbstractMetaFunction* other) @@ -1426,7 +1438,7 @@ void AbstractMetaBuilderPrivate::traverseFunctions(ScopeModelItem scopeItem, } } else if (QPropertySpec* reset = metaClass->propertySpecForReset(metaFunction->name())) { // Property resetter must be in the form "void name()" - if ((!metaFunction->type()) && (metaFunction->arguments().size() == 0)) { + if ((!metaFunction->type()) && metaFunction->arguments().isEmpty()) { *metaFunction += AbstractMetaAttributes::PropertyResetter; metaFunction->setPropertySpec(reset); } @@ -1635,7 +1647,7 @@ bool AbstractMetaBuilderPrivate::setupInheritance(AbstractMetaClass *metaClass) return true; } -void AbstractMetaBuilderPrivate::traverseEnums(ScopeModelItem scopeItem, +void AbstractMetaBuilderPrivate::traverseEnums(const ScopeModelItem &scopeItem, AbstractMetaClass *metaClass, const QStringList &enumsDeclarations) { @@ -1724,13 +1736,9 @@ AbstractMetaFunction* AbstractMetaBuilderPrivate::traverseFunction(const AddedFu replacedExpression = metaFunction->replacedDefaultExpression(m_currentClass, i + 1); if (!replacedExpression.isEmpty()) { - QString expr = replacedExpression; if (!metaFunction->removedDefaultExpression(m_currentClass, i + 1)) { - metaArg->setDefaultValueExpression(expr); - metaArg->setOriginalDefaultValueExpression(expr); - - if (metaArg->type()->isEnum() || metaArg->type()->isFlags()) - m_enumDefaultArguments << QPair<AbstractMetaArgument*, AbstractMetaFunction*>(metaArg, metaFunction); + metaArg->setDefaultValueExpression(replacedExpression); + metaArg->setOriginalDefaultValueExpression(replacedExpression); } } } @@ -1784,7 +1792,7 @@ void AbstractMetaBuilderPrivate::fixArgumentNames(AbstractMetaFunction *func, co } } -static QString functionSignature(FunctionModelItem functionItem) +static QString functionSignature(const FunctionModelItem &functionItem) { QStringList args; const ArgumentList &arguments = functionItem->arguments(); @@ -1802,50 +1810,6 @@ static inline QString qualifiedFunctionSignatureWithType(const FunctionModelItem result += functionSignature(functionItem); return result; } - -static inline QString msgUnmatchedParameterType(const ArgumentModelItem &arg, int n) -{ - QString result; - QTextStream str(&result); - str << "unmatched type '" << arg->type().toString() << "' in parameter #" - << (n + 1); - if (!arg->name().isEmpty()) - str << " \"" << arg->name() << '"'; - return result; -} - -static inline QString msgUnmatchedReturnType(const FunctionModelItem &functionItem) -{ - return QLatin1String("unmatched return type '") - + functionItem->type().toString() + QLatin1Char('\''); -} - -static inline QString msgVoidParameterType(const ArgumentModelItem &arg, int n) -{ - QString result; - QTextStream str(&result); - str << "'void' encountered at parameter #" << (n + 1); - if (!arg->name().isEmpty()) - str << " \"" << arg->name() << '"'; - return result; -} - -static QString msgSkippingFunction(const FunctionModelItem &functionItem, - const QString &signature, const QString &why) -{ - QString result; - QTextStream str(&result); - str << "skipping "; - if (functionItem->isAbstract()) - str << "abstract "; - str << "function '" << signature << "', " << why; - if (functionItem->isAbstract()) { - str << "\nThis will lead to compilation errors due to not " - "being able to instantiate the wrapper."; - } - return result; -} - static inline AbstractMetaFunction::FunctionType functionTypeFromCodeModel(CodeModel::FunctionType ft) { AbstractMetaFunction::FunctionType result = AbstractMetaFunction::NormalFunction; @@ -1874,12 +1838,6 @@ 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) @@ -1911,12 +1869,40 @@ bool AbstractMetaBuilderPrivate::setArrayArgumentType(AbstractMetaFunction *func return true; } -AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(FunctionModelItem functionItem) +static bool generateExceptionHandling(const AbstractMetaFunction *func, + ExceptionSpecification spec, + TypeSystem::ExceptionHandling handling) +{ + switch (func->functionType()) { + case AbstractMetaFunction::CopyConstructorFunction: + case AbstractMetaFunction::MoveConstructorFunction: + case AbstractMetaFunction::AssignmentOperatorFunction: + case AbstractMetaFunction::MoveAssignmentOperatorFunction: + case AbstractMetaFunction::DestructorFunction: + return false; + default: + break; + } + switch (handling) { + case TypeSystem::ExceptionHandling::On: + return true; + case TypeSystem::ExceptionHandling::AutoDefaultToOn: + return spec != ExceptionSpecification::NoExcept; + case TypeSystem::ExceptionHandling::AutoDefaultToOff: + return spec == ExceptionSpecification::Throws; + default: + break; + } + return false; +} + +AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const FunctionModelItem &functionItem) { if (functionItem->isDeleted() || !functionItem->templateParameters().isEmpty()) return nullptr; QString functionName = functionItem->name(); QString className; + TypeSystem::ExceptionHandling exceptionHandling = TypeSystem::ExceptionHandling::Unspecified; if (m_currentClass) { // Clang: Skip qt_metacast(), qt_metacall(), expanded from Q_OBJECT // and overridden metaObject(), QGADGET helpers @@ -1925,6 +1911,7 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(FunctionModel return nullptr; } className = m_currentClass->typeEntry()->qualifiedCppName(); + exceptionHandling = m_currentClass->typeEntry()->exceptionHandling(); if (functionName == QLatin1String("metaObject") && className != QLatin1String("QObject")) return nullptr; } @@ -1936,13 +1923,16 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(FunctionModel QString rejectReason; if (TypeDatabase::instance()->isFunctionRejected(className, functionName, &rejectReason)) { m_rejectedFunctions.insert(originalQualifiedSignatureWithReturn + rejectReason, AbstractMetaBuilder::GenerationDisabled); - return 0; - } - else if (TypeDatabase::instance()->isFunctionRejected(className, - functionSignature(functionItem), &rejectReason)) { - m_rejectedFunctions.insert(originalQualifiedSignatureWithReturn + rejectReason, AbstractMetaBuilder::GenerationDisabled); - return 0; + return nullptr; } + const QString &signature = functionSignature(functionItem); + const bool rejected = + TypeDatabase::instance()->isFunctionRejected(className, signature, &rejectReason); + qCDebug(lcShiboken).nospace().noquote() << __FUNCTION__ + << ": Checking rejection for signature \"" << signature << "\" for " << className + << ": " << rejected; + if (rejected) + return nullptr; if (functionItem->isFriend()) return 0; @@ -1951,6 +1941,7 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(FunctionModel // Additional check for assignment/move assignment down below metaFunction->setFunctionType(functionTypeFromCodeModel(functionItem->functionType())); metaFunction->setConstant(functionItem->isConstant()); + metaFunction->setExceptionSpecification(functionItem->exceptionSpecification()); if (ReportHandler::isDebug(ReportHandler::MediumDebug)) qCDebug(lcShiboken).noquote().nospace() << " - " << functionName << "()"; @@ -1987,6 +1978,7 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(FunctionModel else *metaFunction += AbstractMetaAttributes::Protected; + QString errorMessage; switch (metaFunction->functionType()) { case AbstractMetaFunction::DestructorFunction: break; @@ -2005,9 +1997,9 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(FunctionModel AbstractMetaType *type = nullptr; if (!returnType.isVoid()) { - type = translateType(returnType); + type = translateType(returnType, true, &errorMessage); if (!type) { - const QString reason = msgUnmatchedReturnType(functionItem); + const QString reason = msgUnmatchedReturnType(functionItem, errorMessage); qCWarning(lcShiboken, "%s", qPrintable(msgSkippingFunction(functionItem, originalQualifiedSignatureWithReturn, reason))); m_rejectedFunctions.insert(originalQualifiedSignatureWithReturn, AbstractMetaBuilder::UnmatchedReturnType); @@ -2033,7 +2025,7 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(FunctionModel AbstractMetaArgumentList metaArguments; for (int i = 0; i < arguments.size(); ++i) { - ArgumentModelItem arg = arguments.at(i); + const ArgumentModelItem &arg = arguments.at(i); if (TypeDatabase::instance()->isArgumentTypeRejected(className, arg->type().toString(), &rejectReason)) { m_rejectedFunctions.insert(originalQualifiedSignatureWithReturn + rejectReason, AbstractMetaBuilder::GenerationDisabled); @@ -2041,7 +2033,7 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(FunctionModel return nullptr; } - AbstractMetaType *metaType = translateType(arg->type()); + AbstractMetaType *metaType = translateType(arg->type(), true, &errorMessage); if (!metaType) { // If an invalid argument has a default value, simply remove it if (arg->defaultValue()) { @@ -2058,18 +2050,7 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(FunctionModel break; } Q_ASSERT(metaType == 0); - const QString reason = msgUnmatchedParameterType(arg, i); - qCWarning(lcShiboken, "%s", - qPrintable(msgSkippingFunction(functionItem, originalQualifiedSignatureWithReturn, reason))); - const QString rejectedFunctionSignature = originalQualifiedSignatureWithReturn - + QLatin1String(": ") + reason; - m_rejectedFunctions.insert(rejectedFunctionSignature, AbstractMetaBuilder::UnmatchedArgumentType); - delete metaFunction; - return nullptr; - } - - if (metaType == Q_NULLPTR) { - const QString reason = msgVoidParameterType(arg, i); + const QString reason = msgUnmatchedParameterType(arg, i, errorMessage); qCWarning(lcShiboken, "%s", qPrintable(msgSkippingFunction(functionItem, originalQualifiedSignatureWithReturn, reason))); const QString rejectedFunctionSignature = originalQualifiedSignatureWithReturn @@ -2089,9 +2070,22 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(FunctionModel metaFunction->setArguments(metaArguments); + const FunctionModificationList functionMods = metaFunction->modifications(m_currentClass); + + for (const FunctionModification &mod : functionMods) { + if (mod.exceptionHandling() != TypeSystem::ExceptionHandling::Unspecified) { + exceptionHandling = mod.exceptionHandling(); + break; + } + } + + metaFunction->setGenerateExceptionHandling(generateExceptionHandling(metaFunction, + functionItem->exceptionSpecification(), + exceptionHandling)); + // Find the correct default values for (int i = 0, size = metaArguments.size(); i < size; ++i) { - ArgumentModelItem arg = arguments.at(i); + const ArgumentModelItem &arg = arguments.at(i); AbstractMetaArgument* metaArg = metaArguments.at(i); //use relace-default-expression for set default value @@ -2099,9 +2093,8 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(FunctionModel if (m_currentClass) { replacedExpression = metaFunction->replacedDefaultExpression(m_currentClass, i + 1); } else { - FunctionModificationList mods = TypeDatabase::instance()->functionModifications(metaFunction->minimalSignature()); - if (!mods.isEmpty()) { - QVector<ArgumentModification> argMods = mods.constFirst().argument_mods; + if (!functionMods.isEmpty()) { + QVector<ArgumentModification> argMods = functionMods.constFirst().argument_mods; if (!argMods.isEmpty()) replacedExpression = argMods.constFirst().replacedDefaultExpression; } @@ -2119,10 +2112,6 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(FunctionModel expr = replacedExpression; } metaArg->setDefaultValueExpression(expr); - - if (metaArg->type()->isEnum() || metaArg->type()->isFlags()) - m_enumDefaultArguments << QPair<AbstractMetaArgument *, AbstractMetaFunction *>(metaArg, metaFunction); - hasDefaultValue = !expr.isEmpty(); } @@ -2140,9 +2129,8 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(FunctionModel } if (!metaArguments.isEmpty()) { - const FunctionModificationList &mods = metaFunction->modifications(m_currentClass); - fixArgumentNames(metaFunction, mods); - for (const FunctionModification &mod : mods) { + fixArgumentNames(metaFunction, functionMods); + for (const FunctionModification &mod : functionMods) { for (const ArgumentModification &argMod : mod.argument_mods) { if (argMod.array) setArrayArgumentType(metaFunction, functionItem, argMod.index - 1); @@ -2202,8 +2190,8 @@ AbstractMetaType *AbstractMetaBuilderPrivate::translateType(const AddedFunction: if (!type) { QStringList candidates; - SingleTypeEntryHash entries = typeDb->entries(); - for (SingleTypeEntryHash::const_iterator it = entries.cbegin(), end = entries.cend(); it != end; ++it) { + const auto &entries = typeDb->entries(); + for (auto it = entries.cbegin(), end = entries.cend(); it != end; ++it) { // Let's try to find the type in different scopes. if (it.key().endsWith(colonColon() + typeName)) candidates.append(it.key()); @@ -2211,15 +2199,17 @@ AbstractMetaType *AbstractMetaBuilderPrivate::translateType(const AddedFunction: QString msg = QStringLiteral("Type '%1' wasn't found in the type database.\n").arg(typeName); - if (candidates.isEmpty()) - qFatal(qPrintable(QString(msg + QLatin1String("Declare it in the type system using the proper <*-type> tag."))), NULL); + if (candidates.isEmpty()) { + qFatal("%sDeclare it in the type system using the proper <*-type> tag.", + qPrintable(msg)); + } msg += QLatin1String("Remember to inform the full qualified name for the type you want to use.\nCandidates are:\n"); candidates.sort(); for (const QString& candidate : qAsConst(candidates)) { msg += QLatin1String(" ") + candidate + QLatin1Char('\n'); } - qFatal(qPrintable(msg), NULL); + qFatal("%s", qPrintable(msg)); } AbstractMetaType *metaType = new AbstractMetaType; @@ -2243,7 +2233,7 @@ static const TypeEntry* findTypeEntryUsingContext(const AbstractMetaClass* metaC { const TypeEntry* type = 0; QStringList context = metaClass->qualifiedCppName().split(colonColon()); - while(!type && (context.size() > 0) ) { + while (!type && !context.isEmpty()) { type = TypeDatabase::instance()->findType(context.join(colonColon()) + colonColon() + qualifiedName); context.removeLast(); } @@ -2251,43 +2241,48 @@ static const TypeEntry* findTypeEntryUsingContext(const AbstractMetaClass* metaC } AbstractMetaType *AbstractMetaBuilderPrivate::translateType(const TypeInfo &_typei, - bool resolveType) + bool resolveType, + QString *errorMessage) +{ + return translateTypeStatic(_typei, m_currentClass, this, resolveType, errorMessage); +} + +AbstractMetaType *AbstractMetaBuilderPrivate::translateTypeStatic(const TypeInfo &_typei, + AbstractMetaClass *currentClass, + AbstractMetaBuilderPrivate *d, + bool resolveType, + QString *errorMessageIn) { // 1. Test the type info without resolving typedefs in case this is present in the // type system - TypeInfo typei; if (resolveType) { - if (AbstractMetaType *resolved = translateType(_typei, false)) + if (AbstractMetaType *resolved = translateTypeStatic(_typei, currentClass, d, false, errorMessageIn)) return resolved; } - if (!resolveType) { - typei = _typei; - } else { + TypeInfo typeInfo = _typei; + if (resolveType) { // Go through all parts of the current scope (including global namespace) // to resolve typedefs. The parser does not properly resolve typedefs in // the global scope when they are referenced from inside a namespace. // This is a work around to fix this bug since fixing it in resolveType // seemed non-trivial - int i = m_scopes.size() - 1; + int i = d ? d->m_scopes.size() - 1 : -1; while (i >= 0) { - typei = TypeInfo::resolveType(_typei, m_scopes.at(i--)); - if (typei.qualifiedName().join(colonColon()) != _typei.qualifiedName().join(colonColon())) + typeInfo = TypeInfo::resolveType(_typei, d->m_scopes.at(i--)); + if (typeInfo.qualifiedName().join(colonColon()) != _typei.qualifiedName().join(colonColon())) break; } } - if (typei.isFunctionPointer()) + if (typeInfo.isFunctionPointer()) { + if (errorMessageIn) + *errorMessageIn = msgUnableToTranslateType(_typei, QLatin1String("Unsupported function pointer.")); return nullptr; + } QString errorMessage; - TypeInfo typeInfo = TypeParser::parse(typei.toString(), &errorMessage); - if (typeInfo.qualifiedName().isEmpty()) { - qWarning().noquote().nospace() << "Unable to translate type \"" << _typei.toString() - << "\": " << errorMessage; - return 0; - } // 2. Handle arrays. // 2.1 Handle char arrays with unspecified size (aka "const char[]") as "const char*" with @@ -2312,16 +2307,22 @@ AbstractMetaType *AbstractMetaBuilderPrivate::translateType(const TypeInfo &_typ if (!typeInfo.arrayElements().isEmpty() && !isConstCharStarCase) { TypeInfo newInfo; //newInfo.setArguments(typeInfo.arguments()); - newInfo.setIndirections(typeInfo.indirections()); + newInfo.setIndirectionsV(typeInfo.indirectionsV()); newInfo.setConstant(typeInfo.isConstant()); + newInfo.setVolatile(typeInfo.isVolatile()); newInfo.setFunctionPointer(typeInfo.isFunctionPointer()); newInfo.setQualifiedName(typeInfo.qualifiedName()); newInfo.setReferenceType(typeInfo.referenceType()); newInfo.setVolatile(typeInfo.isVolatile()); - AbstractMetaType *elementType = translateType(newInfo); - if (!elementType) + AbstractMetaType *elementType = translateTypeStatic(newInfo, currentClass, d, true, &errorMessage); + if (!elementType) { + if (errorMessageIn) { + errorMessage.prepend(QLatin1String("Unable to translate array element: ")); + *errorMessageIn = msgUnableToTranslateType(_typei, errorMessage); + } return nullptr; + } for (int i = typeInfo.arrayElements().size() - 1; i >= 0; --i) { AbstractMetaType *arrayType = new AbstractMetaType; @@ -2329,7 +2330,9 @@ AbstractMetaType *AbstractMetaBuilderPrivate::translateType(const TypeInfo &_typ const QString &arrayElement = typeInfo.arrayElements().at(i); if (!arrayElement.isEmpty()) { bool _ok; - const qint64 elems = findOutValueFromString(arrayElement, _ok); + const qint64 elems = d + ? d->findOutValueFromString(arrayElement, _ok) + : arrayElement.toLongLong(&_ok, 0); if (_ok) arrayType->setArrayElementCount(int(elems)); } @@ -2344,8 +2347,11 @@ AbstractMetaType *AbstractMetaBuilderPrivate::translateType(const TypeInfo &_typ QStringList qualifierList = typeInfo.qualifiedName(); if (qualifierList.isEmpty()) { - qCWarning(lcShiboken).noquote().nospace() - << QStringLiteral("horribly broken type '%1'").arg(_typei.toString()); + errorMessage = msgUnableToTranslateType(_typei, QLatin1String("horribly broken type")); + if (errorMessageIn) + *errorMessageIn = errorMessage; + else + qCWarning(lcShiboken,"%s", qPrintable(errorMessage)); return nullptr; } @@ -2353,19 +2359,21 @@ AbstractMetaType *AbstractMetaBuilderPrivate::translateType(const TypeInfo &_typ QString name = qualifierList.takeLast(); // 4. Special case QFlags (include instantiation in name) - if (qualifiedName == QLatin1String("QFlags")) + if (qualifiedName == QLatin1String("QFlags")) { qualifiedName = typeInfo.toString(); + typeInfo.clearInstantiations(); + } const TypeEntry *type = 0; // 5. Try to find the type // 5.1 - Try first using the current scope - if (m_currentClass) { - type = findTypeEntryUsingContext(m_currentClass, qualifiedName); + if (currentClass) { + type = findTypeEntryUsingContext(currentClass, qualifiedName); // 5.1.1 - Try using the class parents' scopes - if (!type && !m_currentClass->baseClassNames().isEmpty()) { - const AbstractMetaClassList &baseClasses = getBaseClasses(m_currentClass); + if (!type && d && !currentClass->baseClassNames().isEmpty()) { + const AbstractMetaClassList &baseClasses = d->getBaseClasses(currentClass); for (const AbstractMetaClass *cls : baseClasses) { type = findTypeEntryUsingContext(cls, qualifiedName); if (type) @@ -2388,36 +2396,40 @@ AbstractMetaType *AbstractMetaBuilderPrivate::translateType(const TypeInfo &_typ // 8. No? Check if the current class is a template and this type is one // of the parameters. - if (!type && m_currentClass) { - const QVector<TypeEntry *> &template_args = m_currentClass->templateArguments(); + if (!type && currentClass) { + const QVector<TypeEntry *> &template_args = currentClass->templateArguments(); for (TypeEntry *te : template_args) { if (te->name() == qualifiedName) type = te; } } - if (!type) + if (!type) { + if (errorMessageIn) { + *errorMessageIn = + msgUnableToTranslateType(_typei, msgCannotFindTypeEntry(qualifiedName)); + } return nullptr; - - // Used to for diagnostics later... - m_usedTypes << type; + } // These are only implicit and should not appear in code... Q_ASSERT(!type->isInterface()); AbstractMetaType *metaType = new AbstractMetaType; metaType->setTypeEntry(type); - metaType->setIndirections(typeInfo.indirections()); + metaType->setIndirectionsV(typeInfo.indirectionsV()); metaType->setReferenceType(typeInfo.referenceType()); metaType->setConstant(typeInfo.isConstant()); + metaType->setVolatile(typeInfo.isVolatile()); metaType->setOriginalTypeDescription(_typei.toString()); - const auto &templateArguments = typeInfo.arguments(); + const auto &templateArguments = typeInfo.instantiations(); for (int t = 0, size = templateArguments.size(); t < size; ++t) { - TypeInfo ti = templateArguments.at(t); - ti.setQualifiedName(ti.instantiationName()); - AbstractMetaType *targType = translateType(ti); + const TypeInfo &ti = templateArguments.at(t); + AbstractMetaType *targType = translateTypeStatic(ti, currentClass, d, true, &errorMessage); if (!targType) { + if (errorMessageIn) + *errorMessageIn = msgCannotTranslateTemplateArgument(t, ti, errorMessage); delete metaType; return nullptr; } @@ -2434,6 +2446,33 @@ AbstractMetaType *AbstractMetaBuilderPrivate::translateType(const TypeInfo &_typ return metaType; } +AbstractMetaType *AbstractMetaBuilder::translateType(const TypeInfo &_typei, + AbstractMetaClass *currentClass, + bool resolveType, + QString *errorMessage) +{ + return AbstractMetaBuilderPrivate::translateTypeStatic(_typei, currentClass, + nullptr, resolveType, + errorMessage); +} + +AbstractMetaType *AbstractMetaBuilder::translateType(const QString &t, + AbstractMetaClass *currentClass, + bool resolveType, + QString *errorMessageIn) +{ + QString errorMessage; + TypeInfo typeInfo = TypeParser::parse(t, &errorMessage); + if (typeInfo.qualifiedName().isEmpty()) { + errorMessage = msgUnableToTranslateType(t, errorMessage); + if (errorMessageIn) + *errorMessageIn = errorMessage; + else + qCWarning(lcShiboken, "%s", qPrintable(errorMessage)); + return nullptr; + } + return translateType(typeInfo, currentClass, resolveType, errorMessageIn); +} qint64 AbstractMetaBuilderPrivate::findOutValueFromString(const QString &stringValue, bool &ok) { @@ -2472,7 +2511,7 @@ qint64 AbstractMetaBuilderPrivate::findOutValueFromString(const QString &stringV return 0; } -QString AbstractMetaBuilderPrivate::fixDefaultValue(ArgumentModelItem item, +QString AbstractMetaBuilderPrivate::fixDefaultValue(const ArgumentModelItem &item, AbstractMetaType *type, AbstractMetaFunction *fnc, AbstractMetaClass *implementingClass, @@ -2676,59 +2715,55 @@ bool AbstractMetaBuilderPrivate::ancestorHasPrivateCopyConstructor(const Abstrac return false; } -AbstractMetaType* AbstractMetaBuilderPrivate::inheritTemplateType(const QVector<AbstractMetaType *> &templateTypes, - const AbstractMetaType *metaType, - bool *ok) +AbstractMetaType * + AbstractMetaBuilderPrivate::inheritTemplateType(const AbstractMetaTypeList &templateTypes, + const AbstractMetaType *metaType) { - if (ok) - *ok = true; - if (!metaType || (!metaType->typeEntry()->isTemplateArgument() && !metaType->hasInstantiations())) - return metaType ? metaType->copy() : 0; + Q_ASSERT(metaType); + + QScopedPointer<AbstractMetaType> returned(metaType->copy()); - AbstractMetaType *returned = metaType->copy(); - returned->setOriginalTemplateType(metaType->copy()); + if (!metaType->typeEntry()->isTemplateArgument() && !metaType->hasInstantiations()) + return returned.take(); + + returned->setOriginalTemplateType(metaType); if (returned->typeEntry()->isTemplateArgument()) { const TemplateArgumentEntry* tae = static_cast<const TemplateArgumentEntry*>(returned->typeEntry()); // If the template is intantiated with void we special case this as rejecting the functions that use this // parameter from the instantiation. - if (templateTypes.size() <= tae->ordinal() || templateTypes.at(tae->ordinal())->typeEntry()->name() == QLatin1String("void")) { - if (ok) - *ok = false; - return 0; - } + const AbstractMetaType *templateType = templateTypes.value(tae->ordinal()); + if (!templateType || templateType->typeEntry()->isVoid()) + return nullptr; AbstractMetaType* t = returned->copy(); - t->setTypeEntry(templateTypes.at(tae->ordinal())->typeEntry()); - t->setIndirections(templateTypes.at(tae->ordinal())->indirections() + t->indirections() ? 1 : 0); + t->setTypeEntry(templateType->typeEntry()); + t->setIndirections(templateType->indirections() + t->indirections() ? 1 : 0); t->decideUsagePattern(); - delete returned; - returned = inheritTemplateType(templateTypes, t, ok); - if (ok && !(*ok)) - return 0; + return inheritTemplateType(templateTypes, t); } if (returned->hasInstantiations()) { AbstractMetaTypeList instantiations = returned->instantiations(); for (int i = 0; i < instantiations.count(); ++i) { - AbstractMetaType *type = instantiations[i]; - instantiations[i] = inheritTemplateType(templateTypes, type, ok); - if (ok && !(*ok)) - return 0; + instantiations[i] = + inheritTemplateType(templateTypes, instantiations.at(i)); + if (!instantiations.at(i)) + return nullptr; } returned->setInstantiations(instantiations, true); } - return returned; + return returned.take(); } bool AbstractMetaBuilderPrivate::inheritTemplate(AbstractMetaClass *subclass, const AbstractMetaClass *templateClass, const TypeInfo &info) { - QVector<TypeInfo> targs = info.arguments(); + QVector<TypeInfo> targs = info.instantiations(); QVector<AbstractMetaType *> templateTypes; if (subclass->isTypeDef()) { @@ -2743,20 +2778,35 @@ bool AbstractMetaBuilderPrivate::inheritTemplate(AbstractMetaClass *subclass, for (const TypeInfo &i : qAsConst(targs)) { QString typeName = i.qualifiedName().join(colonColon()); - QStringList possibleNames; - possibleNames << subclass->qualifiedCppName() + colonColon() + typeName; - possibleNames << templateClass->qualifiedCppName() + colonColon() + typeName; - if (subclass->enclosingClass()) - possibleNames << subclass->enclosingClass()->qualifiedCppName() + colonColon() + typeName; - possibleNames << typeName; - - TypeDatabase* typeDb = TypeDatabase::instance(); - TypeEntry* t = 0; - QString templateParamName; - for (const QString &possibleName : qAsConst(possibleNames)) { - t = typeDb->findType(possibleName); - if (t) - break; + TypeDatabase *typeDb = TypeDatabase::instance(); + TypeEntry *t = nullptr; + // Check for a non-type template integer parameter, that is, for a base + // "template <int R, int C> Matrix<R, C>" and subclass + // "typedef Matrix<2,3> Matrix2x3;". If so, create dummy entries of + // EnumValueTypeEntry for the integer values encountered on the fly. + const bool isNumber = std::all_of(typeName.cbegin(), typeName.cend(), + [](QChar c) { return c.isDigit(); }); + if (isNumber) { + t = typeDb->findType(typeName); + if (!t) { + t = new EnumValueTypeEntry(typeName, typeName, nullptr, + QVersionNumber(0, 0)); + t->setCodeGeneration(0); + typeDb->addType(t); + } + } else { + QStringList possibleNames; + possibleNames << subclass->qualifiedCppName() + colonColon() + typeName; + possibleNames << templateClass->qualifiedCppName() + colonColon() + typeName; + if (subclass->enclosingClass()) + possibleNames << subclass->enclosingClass()->qualifiedCppName() + colonColon() + typeName; + possibleNames << typeName; + + for (const QString &possibleName : qAsConst(possibleNames)) { + t = typeDb->findType(possibleName); + if (t) + break; + } } if (t) { @@ -2764,48 +2814,48 @@ bool AbstractMetaBuilderPrivate::inheritTemplate(AbstractMetaClass *subclass, temporaryType->setTypeEntry(t); temporaryType->setConstant(i.isConstant()); temporaryType->setReferenceType(i.referenceType()); - temporaryType->setIndirections(i.indirections()); + temporaryType->setIndirectionsV(i.indirectionsV()); temporaryType->decideUsagePattern(); templateTypes << temporaryType; } else { qCWarning(lcShiboken).noquote().nospace() - << "Ignoring template parameter " << templateParamName << " from " - << info.instantiationName() << ", because I don't know what it is."; + << "Ignoring template parameter " << typeName << " from " + << info.toString() << ". The corresponding type was not found in the typesystem."; } } - AbstractMetaFunctionList funcs = subclass->functions(); + const AbstractMetaFunctionList &subclassFuncs = subclass->functions(); const AbstractMetaFunctionList &templateClassFunctions = templateClass->functions(); for (const AbstractMetaFunction *function : templateClassFunctions) { - if (function->isModifiedRemoved(TypeSystem::All)) + // If the function is modified or the instantiation has an equally named + // function we have shadowing, so we need to skip it. + if (function->isModifiedRemoved(TypeSystem::All) + || AbstractMetaFunction::find(subclassFuncs, function->name()) != nullptr) { continue; + } - AbstractMetaFunction *f = function->copy(); + QScopedPointer<AbstractMetaFunction> f(function->copy()); f->setArguments(AbstractMetaArgumentList()); - bool ok = true; - AbstractMetaType *ftype = function->type(); - f->replaceType(inheritTemplateType(templateTypes, ftype, &ok)); - if (!ok) { - delete f; - continue; + if (function->type()) { // Non-void + AbstractMetaType *returnType = inheritTemplateType(templateTypes, function->type()); + if (!returnType) + continue; + f->replaceType(returnType); } const AbstractMetaArgumentList &arguments = function->arguments(); for (AbstractMetaArgument *argument : arguments) { - AbstractMetaType* atype = argument->type(); - - AbstractMetaArgument *arg = argument->copy(); - arg->replaceType(inheritTemplateType(templateTypes, atype, &ok)); - if (!ok) + AbstractMetaType *argType = inheritTemplateType(templateTypes, argument->type()); + if (!argType) break; + AbstractMetaArgument *arg = argument->copy(); + arg->replaceType(argType); f->addArgument(arg); } - if (!ok) { - delete f; + if (f->arguments().size() < function->arguments().size()) continue; - } // There is no base class in the target language to inherit from here, so // the template instantiation is the class that implements the function. @@ -2816,27 +2866,11 @@ bool AbstractMetaBuilderPrivate::inheritTemplate(AbstractMetaClass *subclass, // on the inherited functions. f->setDeclaringClass(subclass); - - if (f->isConstructor() && subclass->isTypeDef()) { + if (f->isConstructor()) { + if (!subclass->isTypeDef()) + continue; f->setName(subclass->name()); f->setOriginalName(subclass->name()); - } else if (f->isConstructor()) { - delete f; - continue; - } - - // if the instantiation has a function named the same as an existing - // function we have shadowing so we need to skip it. - bool found = false; - for (int i = 0; i < funcs.size(); ++i) { - if (funcs.at(i)->name() == f->name()) { - found = true; - continue; - } - } - if (found) { - delete f; - continue; } ComplexTypeEntry* te = subclass->typeEntry(); @@ -2863,7 +2897,27 @@ bool AbstractMetaBuilderPrivate::inheritTemplate(AbstractMetaClass *subclass, te->addFunctionModification(mod); } - subclass->addFunction(f); + subclass->addFunction(f.take()); + } + + const AbstractMetaFieldList &subClassFields = subclass->fields(); + const AbstractMetaFieldList &templateClassFields = templateClass->fields(); + for (const AbstractMetaField *field : templateClassFields) { + // If the field is modified or the instantiation has a field named + // the same as an existing field we have shadowing, so we need to skip it. + if (field->isModifiedRemoved(TypeSystem::All) + || field->attributes().testFlag(AbstractMetaAttributes::Static) + || AbstractMetaField::find(subClassFields, field->name()) != nullptr) { + continue; + } + + QScopedPointer<AbstractMetaField> f(field->copy()); + f->setEnclosingClass(subclass); + AbstractMetaType *fieldType = inheritTemplateType(templateTypes, field->type()); + if (!fieldType) + continue; + f->replaceType(fieldType); + subclass->addField(f.take()); } subclass->setTemplateBaseClass(templateClass); @@ -2878,9 +2932,9 @@ void AbstractMetaBuilderPrivate::parseQ_Property(AbstractMetaClass *metaClass, const QStringList &declarations) { for (int i = 0; i < declarations.size(); ++i) { - QString p = declarations.at(i); + const QString &p = declarations.at(i); - QStringList l = p.split(QLatin1String(" ")); + QStringList l = p.split(QLatin1Char(' ')); QStringList qualifiedScopeName = currentScope()->qualifiedName(); @@ -2925,8 +2979,8 @@ void AbstractMetaBuilderPrivate::parseQ_Property(AbstractMetaClass *metaClass, static AbstractMetaFunction* findCopyCtor(AbstractMetaClass* cls) { - AbstractMetaFunctionList functions = cls->queryFunctions(AbstractMetaClass::Invisible); - functions << cls->queryFunctions(AbstractMetaClass::Visible); + + const auto &functions = cls->functions(); for (AbstractMetaFunction *f : qAsConst(functions)) { const AbstractMetaFunction::FunctionType t = f->functionType(); diff --git a/sources/shiboken2/ApiExtractor/abstractmetabuilder.h b/sources/shiboken2/ApiExtractor/abstractmetabuilder.h index a0ca71b94..01806f6b4 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetabuilder.h +++ b/sources/shiboken2/ApiExtractor/abstractmetabuilder.h @@ -38,7 +38,10 @@ QT_FORWARD_DECLARE_CLASS(QIODevice) class AbstractMetaBuilderPrivate; class AbstractMetaClass; +class AbstractMetaType; class AbstractMetaEnumValue; +class TypeInfo; +class TypeEntry; class AbstractMetaBuilder { @@ -61,6 +64,7 @@ public: AbstractMetaClassList smartPointers() const; AbstractMetaFunctionList globalFunctions() const; AbstractMetaEnumList globalEnums() const; + AbstractMetaEnum *findEnum(const TypeEntry *typeEntry) const; /** * Sorts a list of classes topologically, if an AbstractMetaClass object @@ -83,6 +87,16 @@ public: */ void setGlobalHeader(const QString& globalHeader); + static AbstractMetaType *translateType(const TypeInfo &_typei, + AbstractMetaClass *currentClass = nullptr, + bool resolveType = true, + QString *errorMessage = nullptr); + static AbstractMetaType *translateType(const QString &t, + AbstractMetaClass *currentClass = nullptr, + bool resolveType = true, + QString *errorMessage = nullptr); + + #ifndef QT_NO_DEBUG_STREAM void formatDebug(QDebug &d) const; #endif diff --git a/sources/shiboken2/ApiExtractor/abstractmetabuilder_p.h b/sources/shiboken2/ApiExtractor/abstractmetabuilder_p.h index 59e3cfc94..ec55d1b47 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetabuilder_p.h +++ b/sources/shiboken2/ApiExtractor/abstractmetabuilder_p.h @@ -60,11 +60,12 @@ public: ScopeModelItem currentScope() const { return m_scopes.constLast(); } - AbstractMetaClass *argumentToClass(ArgumentModelItem); + AbstractMetaClass *argumentToClass(const ArgumentModelItem &); void addAbstractMetaClass(AbstractMetaClass *cls); AbstractMetaClass *traverseTypeDef(const FileModelItem &dom, const TypeDefModelItem &typeDef); + void traverseTypesystemTypedefs(); AbstractMetaClass *traverseClass(const FileModelItem &dom, const ClassModelItem &item); AbstractMetaClass *currentTraversedClass(ScopeModelItem item); @@ -74,9 +75,9 @@ public: bool setupInheritance(AbstractMetaClass *metaClass); AbstractMetaClass *traverseNamespace(const FileModelItem &dom, const NamespaceModelItem &item); - AbstractMetaEnum *traverseEnum(EnumModelItem item, AbstractMetaClass *enclosing, + AbstractMetaEnum *traverseEnum(const EnumModelItem &item, AbstractMetaClass *enclosing, const QSet<QString> &enumsDeclarations); - void traverseEnums(ScopeModelItem item, AbstractMetaClass *parent, + void traverseEnums(const ScopeModelItem &item, AbstractMetaClass *parent, const QStringList &enumsDeclarations); AbstractMetaFunctionList classFunctionList(const ScopeModelItem &scopeItem, bool *constructorRejected); @@ -85,18 +86,18 @@ public: bool *constructorRejected); void traverseFunctions(ScopeModelItem item, AbstractMetaClass *parent); void applyFunctionModifications(AbstractMetaFunction* func); - void traverseFields(ScopeModelItem item, AbstractMetaClass *parent); - void traverseStreamOperator(FunctionModelItem functionItem); - void traverseOperatorFunction(FunctionModelItem item); + void traverseFields(const ScopeModelItem &item, AbstractMetaClass *parent); + void traverseStreamOperator(const FunctionModelItem &functionItem); + void traverseOperatorFunction(const FunctionModelItem &item); AbstractMetaFunction* traverseFunction(const AddedFunction &addedFunc); AbstractMetaFunction* traverseFunction(const AddedFunction &addedFunc, AbstractMetaClass *metaClass); - AbstractMetaFunction *traverseFunction(FunctionModelItem function); - AbstractMetaField *traverseField(VariableModelItem field, + AbstractMetaFunction *traverseFunction(const FunctionModelItem &function); + AbstractMetaField *traverseField(const VariableModelItem &field, const AbstractMetaClass *cls); void checkFunctionModifications(); - void registerHashFunction(FunctionModelItem functionItem); - void registerToStringCapability(FunctionModelItem functionItem); + void registerHashFunction(const FunctionModelItem &functionItem); + void registerToStringCapability(const FunctionModelItem &functionItem); /** * A conversion operator function should not have its owner class as @@ -118,12 +119,19 @@ public: void setupFunctionDefaults(AbstractMetaFunction *metaFunction, AbstractMetaClass *metaClass); - QString fixDefaultValue(ArgumentModelItem item, AbstractMetaType *type, + QString fixDefaultValue(const ArgumentModelItem &item, AbstractMetaType *type, AbstractMetaFunction *fnc, AbstractMetaClass *, int argumentIndex); AbstractMetaType *translateType(const AddedFunction::TypeInfo &typeInfo); AbstractMetaType *translateType(const TypeInfo &type, - bool resolveType = true); + bool resolveType = true, + QString *errorMessage = nullptr); + static AbstractMetaType *translateTypeStatic(const TypeInfo &type, + AbstractMetaClass *current, + AbstractMetaBuilderPrivate *d = nullptr, + bool resolveType = true, + QString *errorMessageIn = nullptr); + qint64 findOutValueFromString(const QString &stringValue, bool &ok); AbstractMetaClass *findTemplateClass(const QString& name, const AbstractMetaClass *context, @@ -135,9 +143,8 @@ public: bool inheritTemplate(AbstractMetaClass *subclass, const AbstractMetaClass *templateClass, const TypeInfo &info); - AbstractMetaType *inheritTemplateType(const QVector<AbstractMetaType *> &templateTypes, - const AbstractMetaType *metaType, - bool *ok = Q_NULLPTR); + AbstractMetaType *inheritTemplateType(const AbstractMetaTypeList &templateTypes, + const AbstractMetaType *metaType); bool isQObject(const FileModelItem &dom, const QString &qualifiedName); bool isEnum(const FileModelItem &dom, const QStringList &qualifiedName); @@ -161,8 +168,6 @@ public: AbstractMetaFunctionList m_globalFunctions; AbstractMetaEnumList m_globalEnums; - QSet<const TypeEntry *> m_usedTypes; - typedef QMap<QString, AbstractMetaBuilder::RejectReason> RejectMap; RejectMap m_rejectedClasses; @@ -170,11 +175,7 @@ public: RejectMap m_rejectedFunctions; RejectMap m_rejectedFields; - QList<AbstractMetaEnum *> m_enums; - - QList<QPair<AbstractMetaArgument *, AbstractMetaFunction *> > m_enumDefaultArguments; - - QHash<QString, AbstractMetaEnumValue *> m_enumValues; + QHash<const TypeEntry *, AbstractMetaEnum *> m_enums; AbstractMetaClass *m_currentClass; QList<ScopeModelItem> m_scopes; diff --git a/sources/shiboken2/ApiExtractor/abstractmetalang.cpp b/sources/shiboken2/ApiExtractor/abstractmetalang.cpp index 5be7050bf..c65d7e0bd 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetalang.cpp +++ b/sources/shiboken2/ApiExtractor/abstractmetalang.cpp @@ -27,10 +27,13 @@ ****************************************************************************/ #include "abstractmetalang.h" +#include "messages.h" #include "reporthandler.h" #include "typedatabase.h" #include "typesystem.h" +#include <parser/codemodel.h> + #ifndef QT_NO_DEBUG_STREAM # include <QtCore/QMetaEnum> # include <QtCore/QMetaObject> @@ -55,6 +58,16 @@ QDebug operator<<(QDebug d, const AbstractMetaAttributes *aa) } #endif // !QT_NO_DEBUG_STREAM +template <class MetaClass> +MetaClass *findByName(QVector<MetaClass *> haystack, QStringView needle) +{ + for (MetaClass *c : haystack) { + if (c->name() == needle) + return c; + } + return nullptr; +} + /******************************************************************************* * AbstractMetaVariable */ @@ -112,8 +125,8 @@ void AbstractMetaAttributes::assignMetaAttributes(const AbstractMetaAttributes & AbstractMetaType::AbstractMetaType() : m_constant(false), + m_volatile(false), m_cppInstantiation(true), - m_indirections(0), m_reserved(0) { } @@ -155,8 +168,9 @@ AbstractMetaType *AbstractMetaType::copy() const cpy->setTypeUsagePattern(typeUsagePattern()); cpy->setConstant(isConstant()); + cpy->setVolatile(isVolatile()); cpy->setReferenceType(referenceType()); - cpy->setIndirections(indirections()); + cpy->setIndirectionsV(indirectionsV()); cpy->setInstantiations(instantiations()); cpy->setArrayElementCount(arrayElementCount()); cpy->setOriginalTypeDescription(originalTypeDescription()); @@ -278,6 +292,26 @@ bool AbstractMetaType::hasTemplateChildren() const return false; } +bool AbstractMetaType::equals(const AbstractMetaType &rhs) const +{ + if (m_typeEntry != rhs.m_typeEntry || m_constant != rhs.m_constant + || m_referenceType != rhs.m_referenceType + || m_indirections != rhs.m_indirections + || m_instantiations.size() != rhs.m_instantiations.size() + || m_arrayElementCount != rhs.m_arrayElementCount) { + return false; + } + if ((m_arrayElementType != nullptr) != (rhs.m_arrayElementType != nullptr) + || (m_arrayElementType != nullptr && !m_arrayElementType->equals(*rhs.m_arrayElementType))) { + return false; + } + for (int i = 0, size = m_instantiations.size(); i < size; ++i) { + if (!m_instantiations.at(i)->equals(*rhs.m_instantiations.at(i))) + return false; + } + return true; +} + #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug d, const AbstractMetaType *at) { @@ -291,16 +325,32 @@ QDebug operator<<(QDebug d, const AbstractMetaType *at) d << ", typeEntry=" << at->typeEntry() << ", signature=\"" << at->cppSignature() << "\", pattern=" << at->typeUsagePattern(); - if (at->indirections()) - d << ", indirections=" << at->indirections(); + const auto indirections = at->indirectionsV(); + if (!indirections.isEmpty()) { + d << ", indirections="; + for (auto i : indirections) + d << ' ' << TypeInfo::indirectionKeyword(i); + } if (at->referenceType()) d << ", reftype=" << at->referenceType(); if (at->isConstant()) d << ", [const]"; + if (at->isVolatile()) + d << ", [volatile]"; if (at->isArray()) { d << ", array of \"" << at->arrayElementType()->cppSignature() << "\", arrayElementCount=" << at->arrayElementCount(); } + const auto &instantiations = at->instantiations(); + if (const int instantiationsSize = instantiations.size()) { + d << ", instantiations[" << instantiationsSize << "]=<"; + for (int i = 0; i < instantiationsSize; ++i) { + if (i) + d << ", "; + d << instantiations.at(i); + } + } + d << '>'; } } else { d << '0'; @@ -357,7 +407,8 @@ AbstractMetaFunction::AbstractMetaFunction() m_userAdded(false), m_explicit(false), m_pointerOperator(false), - m_isCallOperator(false) + m_isCallOperator(false), + m_generateExceptionHandling(false) { } @@ -475,6 +526,8 @@ AbstractMetaFunction *AbstractMetaFunction::copy() const if (type()) cpy->setType(type()->copy()); cpy->setConstant(isConstant()); + cpy->setExceptionSpecification(m_exceptionSpecification); + cpy->setGenerateExceptionHandling(m_generateExceptionHandling); for (AbstractMetaArgument *arg : m_arguments) cpy->addArgument(arg->copy()); @@ -504,18 +557,17 @@ QStringList AbstractMetaFunction::introspectionCompatibleSignatures(const QStrin if (arguments.size() == resolvedArguments.size()) { QString signature = name() + QLatin1Char('(') + resolvedArguments.join(QLatin1Char(',')) + QLatin1Char(')'); return QStringList(TypeDatabase::normalizedSignature(signature)); - } else { - QStringList returned; - - AbstractMetaArgument *argument = arguments.at(resolvedArguments.size()); - QStringList minimalTypeSignature = argument->type()->minimalSignature().split(QLatin1String("::")); - for (int i = 0; i < minimalTypeSignature.size(); ++i) { - returned += introspectionCompatibleSignatures(QStringList(resolvedArguments) - << QStringList(minimalTypeSignature.mid(minimalTypeSignature.size() - i - 1)).join(QLatin1String("::"))); - } + } + QStringList returned; - return returned; + AbstractMetaArgument *argument = arguments.at(resolvedArguments.size()); + QStringList minimalTypeSignature = argument->type()->minimalSignature().split(QLatin1String("::")); + for (int i = 0; i < minimalTypeSignature.size(); ++i) { + returned += introspectionCompatibleSignatures(QStringList(resolvedArguments) + << QStringList(minimalTypeSignature.mid(minimalTypeSignature.size() - i - 1)).join(QLatin1String("::"))); } + + return returned; } QString AbstractMetaFunction::signature() const @@ -674,38 +726,54 @@ bool AbstractMetaFunction::argumentRemoved(int key) const return false; } -bool AbstractMetaFunction::isVirtualSlot() const +bool AbstractMetaFunction::isDeprecated() const { const FunctionModificationList &modifications = this->modifications(declaringClass()); for (const FunctionModification &modification : modifications) { - if (modification.isVirtualSlot()) + if (modification.isDeprecated()) return true; } - return false; } -bool AbstractMetaFunction::isDeprecated() const +// Auto-detect whether a function should be wrapped into +// Py_BEGIN_ALLOW_THREADS/Py_END_ALLOW_THREADS, that is, temporarily release +// the GIL (global interpreter lock). Doing so is required for any thread-wait +// functions, anything that might call a virtual function (potentially +// reimplemented in Python), and recommended for lengthy I/O or similar. +// It has performance costs, though. +bool AbstractMetaFunction::autoDetectAllowThread() const { - const FunctionModificationList &modifications = this->modifications(declaringClass()); - for (const FunctionModification &modification : modifications) { - if (modification.isDeprecated()) - return true; - } - return false; + // Disallow for simple getter functions. + const bool maybeGetter = m_constant != 0 && m_type != nullptr + && m_arguments.isEmpty(); + return !maybeGetter; } bool AbstractMetaFunction::allowThread() const { - const FunctionModificationList &modifications = this->modifications(declaringClass()); - for (const FunctionModification &modification : modifications) { - if (modification.allowThread()) - return true; + using AllowThread = TypeSystem::AllowThread; + + if (m_cachedAllowThread < 0) { + AllowThread allowThread = AllowThread::Auto; + // Find a modification that specifies allowThread + const FunctionModificationList &modifications = this->modifications(declaringClass()); + for (const FunctionModification &modification : modifications) { + if (modification.allowThread() != AllowThread::Unspecified) { + allowThread = modification.allowThread(); + break; + } + } + + m_cachedAllowThread = allowThread == AllowThread::Allow + || (allowThread == AllowThread::Auto && autoDetectAllowThread()) ? 1 : 0; + + if (m_cachedAllowThread == 0) + qCDebug(lcShiboken).noquote() << msgDisallowThread(this); } - return false; + return m_cachedAllowThread > 0; } - TypeSystem::Ownership AbstractMetaFunction::ownership(const AbstractMetaClass *cls, TypeSystem::Language language, int key) const { const FunctionModificationList &modifications = this->modifications(cls); @@ -751,6 +819,18 @@ QString AbstractMetaFunction::typeReplaced(int key) const return QString(); } +bool AbstractMetaFunction::isModifiedToArray(int argumentIndex) const +{ + const FunctionModificationList &modifications = this->modifications(declaringClass()); + for (const FunctionModification &modification : modifications) { + for (const ArgumentModification &argumentModification : modification.argument_mods) { + if (argumentModification.index == argumentIndex && argumentModification.array != 0) + return true; + } + } + return false; +} + QString AbstractMetaFunction::minimalSignature() const { if (!m_cachedMinimalSignature.isEmpty()) @@ -809,8 +889,9 @@ FunctionModificationList AbstractMetaFunction::modifications(const AbstractMetaC while (implementor) { mods += implementor->typeEntry()->functionModifications(minimalSignature()); if ((implementor == implementor->baseClass()) || - (implementor == implementingClass() && (mods.size() > 0))) + (implementor == implementingClass() && !mods.isEmpty())) { break; + } const AbstractMetaClassList &interfaces = implementor->interfaces(); for (const AbstractMetaClass *interface : interfaces) mods += this->modifications(interface); @@ -873,14 +954,24 @@ bool AbstractMetaFunction::hasSignatureModifications() const return false; } -bool AbstractMetaFunction::isConversionOperator(QString funcName) +bool AbstractMetaFunction::isConversionOperator(const QString& funcName) { static const QRegularExpression opRegEx(QStringLiteral("^operator(?:\\s+(?:const|volatile))?\\s+(\\w+\\s*)&?$")); Q_ASSERT(opRegEx.isValid()); return opRegEx.match(funcName).hasMatch(); } -bool AbstractMetaFunction::isOperatorOverload(QString funcName) +ExceptionSpecification AbstractMetaFunction::exceptionSpecification() const +{ + return m_exceptionSpecification; +} + +void AbstractMetaFunction::setExceptionSpecification(ExceptionSpecification e) +{ + m_exceptionSpecification = e; +} + +bool AbstractMetaFunction::isOperatorOverload(const QString& funcName) { if (isConversionOperator(funcName)) return true; @@ -1038,6 +1129,13 @@ bool function_sorter(AbstractMetaFunction *a, AbstractMetaFunction *b) return a->signature() < b->signature(); } +AbstractMetaFunction * +AbstractMetaFunction::find(const AbstractMetaFunctionList &haystack, + const QString &needle) +{ + return findByName(haystack, needle); +} + #ifndef QT_NO_DEBUG_STREAM static inline void formatMetaFunctionBrief(QDebug &d, const AbstractMetaFunction *af) { @@ -1046,7 +1144,20 @@ static inline void formatMetaFunctionBrief(QDebug &d, const AbstractMetaFunction void AbstractMetaFunction::formatDebugVerbose(QDebug &d) const { - d << m_functionType << ' ' << m_type << ' ' << m_name << '('; + d << m_functionType << ' ' << m_type << ' ' << m_name; + switch (m_exceptionSpecification) { + case ExceptionSpecification::Unknown: + break; + case ExceptionSpecification::NoExcept: + d << " noexcept"; + break; + case ExceptionSpecification::Throws: + d << " throw(...)"; + break; + } + if (m_generateExceptionHandling) + d << "[generate-exception-handling]"; + d << '('; for (int i = 0, count = m_arguments.size(); i < count; ++i) { if (i) d << ", "; @@ -1106,7 +1217,6 @@ AbstractMetaClass::AbstractMetaClass() : m_hasVirtuals(false), m_isPolymorphic(false), m_hasNonpublic(false), - m_hasVirtualSlots(false), m_hasNonPrivateConstructor(false), m_hasPrivateConstructor(false), m_functionsFixed(false), @@ -1330,11 +1440,8 @@ void AbstractMetaClass::setFunctions(const AbstractMetaFunctionList &functions) for (AbstractMetaFunction *f : qAsConst(m_functions)) { f->setOwnerClass(this); - - m_hasVirtualSlots = m_hasVirtualSlots || f->isVirtualSlot(); - m_hasVirtuals = m_hasVirtuals || f->isVirtualSlot() || hasVirtualDestructor(); - m_isPolymorphic = m_isPolymorphic || m_hasVirtuals; - m_hasNonpublic = m_hasNonpublic || !f->isPublic(); + if (!f->isPublic()) + m_hasNonpublic = true; } } @@ -1368,8 +1475,7 @@ void AbstractMetaClass::addFunction(AbstractMetaFunction *function) else Q_ASSERT(false); //memory leak - m_hasVirtualSlots |= function->isVirtualSlot(); - m_hasVirtuals |= function->isVirtual() || function->isVirtualSlot() || hasVirtualDestructor(); + m_hasVirtuals |= function->isVirtual() || hasVirtualDestructor(); m_isPolymorphic |= m_hasVirtuals; m_hasNonpublic |= !function->isPublic(); } @@ -1432,11 +1538,7 @@ bool AbstractMetaClass::hasFunction(const QString &str) const const AbstractMetaFunction* AbstractMetaClass::findFunction(const QString& functionName) const { - for (const AbstractMetaFunction *f : m_functions) { - if (f->name() == functionName) - return f; - } - return 0; + return AbstractMetaFunction::find(m_functions, functionName); } bool AbstractMetaClass::hasProtectedFunctions() const @@ -1511,6 +1613,13 @@ void AbstractMetaClass::setTemplateBaseClassInstantiations(AbstractMetaTypeList& metaClassBaseTemplateInstantiations()->insert(this, instantiations); } +// Does any of the base classes require deletion in the main thread? +bool AbstractMetaClass::deleteInMainThread() const +{ + return typeEntry()->deleteInMainThread() + || (m_baseClass && m_baseClass->deleteInMainThread()); +} + static bool functions_contains(const AbstractMetaFunctionList &l, const AbstractMetaFunction *func) { for (const AbstractMetaFunction *f : l) { @@ -1537,6 +1646,11 @@ AbstractMetaField *AbstractMetaField::copy() const return returned; } +AbstractMetaField *AbstractMetaField::find(const AbstractMetaFieldList &haystack, + const QString &needle) +{ + return findByName(haystack, needle); +} /******************************************************************************* * Indicates that this field has a modification that removes it */ @@ -1723,27 +1837,22 @@ QDebug operator<<(QDebug d, const AbstractMetaEnum *ae) bool AbstractMetaClass::hasConstructors() const { - return queryFunctions(Constructors).size(); + return AbstractMetaClass::queryFirstFunction(m_functions, Constructors) != nullptr; } -bool AbstractMetaClass::hasCopyConstructor() const +const AbstractMetaFunction *AbstractMetaClass::copyConstructor() const { - const AbstractMetaFunctionList &ctors = queryFunctions(Constructors); - for (const AbstractMetaFunction* ctor : ctors) { - if (ctor->functionType() == AbstractMetaFunction::CopyConstructorFunction) - return true; + for (const AbstractMetaFunction *f : m_functions) { + if (f->functionType() == AbstractMetaFunction::CopyConstructorFunction) + return f; } - return false; + return nullptr; } bool AbstractMetaClass::hasPrivateCopyConstructor() const { - const AbstractMetaFunctionList &ctors = queryFunctions(Constructors); - for (const AbstractMetaFunction *ctor : ctors) { - if (ctor->functionType() == AbstractMetaFunction::CopyConstructorFunction && ctor->isPrivate()) - return true; - } - return false; + const AbstractMetaFunction *copyCt = copyConstructor(); + return copyCt && copyCt->isPrivate(); } void AbstractMetaClass::addDefaultConstructor() @@ -1801,85 +1910,122 @@ bool AbstractMetaClass::hasFunction(const AbstractMetaFunction *f) const return functions_contains(m_functions, f); } +bool AbstractMetaClass::generateExceptionHandling() const +{ + return queryFirstFunction(m_functions, AbstractMetaClass::Visible + | AbstractMetaClass::GenerateExceptionHandling) != nullptr; +} /* Goes through the list of functions and returns a list of all functions matching all of the criteria in \a query. */ -AbstractMetaFunctionList AbstractMetaClass::queryFunctions(FunctionQueryOptions query) const +bool AbstractMetaClass::queryFunction(const AbstractMetaFunction *f, FunctionQueryOptions query) { - AbstractMetaFunctionList functions; - - for (AbstractMetaFunction *f : m_functions) { - if ((query & NotRemovedFromTargetLang) && f->isRemovedFrom(f->implementingClass(), TypeSystem::TargetLangCode)) - continue; + if ((query & NotRemovedFromTargetLang) + && f->isRemovedFrom(f->implementingClass(), TypeSystem::TargetLangCode)) { + return false; + } - if ((query & NotRemovedFromTargetLang) && f->isVirtual() && f->isRemovedFrom(f->declaringClass(), TypeSystem::TargetLangCode)) - continue; + if ((query & NotRemovedFromTargetLang) && f->isVirtual() + && f->isRemovedFrom(f->declaringClass(), TypeSystem::TargetLangCode)) { + return false; + } - if ((query & Visible) && f->isPrivate()) - continue; + if ((query & Visible) && f->isPrivate()) + return false; - if ((query & VirtualInTargetLangFunctions) && f->isFinalInTargetLang()) - continue; + if ((query & VirtualInTargetLangFunctions) && f->isFinalInTargetLang()) + return false; - if ((query & Invisible) && !f->isPrivate()) - continue; + if ((query & Invisible) && !f->isPrivate()) + return false; - if ((query & Empty) && !f->isEmptyFunction()) - continue; + if ((query & Empty) && !f->isEmptyFunction()) + return false; - if ((query & WasPublic) && !f->wasPublic()) - continue; + if ((query & WasPublic) && !f->wasPublic()) + return false; - if ((query & ClassImplements) && f->ownerClass() != f->implementingClass()) - continue; + if ((query & ClassImplements) && f->ownerClass() != f->implementingClass()) + return false; - if ((query & FinalInTargetLangFunctions) && !f->isFinalInTargetLang()) - continue; + if ((query & FinalInTargetLangFunctions) && !f->isFinalInTargetLang()) + return false; - if ((query & VirtualInCppFunctions) && !f->isVirtual()) - continue; + if ((query & VirtualInCppFunctions) && !f->isVirtual()) + return false; - if ((query & Signals) && (!f->isSignal())) - continue; + if ((query & Signals) && (!f->isSignal())) + return false; - if ((query & Constructors) && (!f->isConstructor() || f->ownerClass() != f->implementingClass())) - continue; + if ((query & Constructors) && (!f->isConstructor() || f->ownerClass() != f->implementingClass())) + return false; - if (!(query & Constructors) && f->isConstructor()) - continue; + if (!(query & Constructors) && f->isConstructor()) + return false; - // Destructors are never included in the functions of a class currently - /* + // Destructors are never included in the functions of a class currently + /* if ((query & Destructors) && (!f->isDestructor() || f->ownerClass() != f->implementingClass()) || f->isDestructor() && (query & Destructors) == 0) { - continue; + return false; }*/ - if ((query & StaticFunctions) && (!f->isStatic() || f->isSignal())) - continue; + if ((query & StaticFunctions) && (!f->isStatic() || f->isSignal())) + return false; - if ((query & NonStaticFunctions) && (f->isStatic())) - continue; + if ((query & NonStaticFunctions) && (f->isStatic())) + return false; - if ((query & NormalFunctions) && (f->isSignal())) - continue; + if ((query & NormalFunctions) && (f->isSignal())) + return false; - if ((query & OperatorOverloads) && !f->isOperatorOverload()) - continue; + if ((query & OperatorOverloads) && !f->isOperatorOverload()) + return false; + + if ((query & GenerateExceptionHandling) && !f->generateExceptionHandling()) + return false; + + return true; +} - functions << f; +AbstractMetaFunctionList AbstractMetaClass::queryFunctionList(const AbstractMetaFunctionList &list, + FunctionQueryOptions query) +{ + AbstractMetaFunctionList result; + for (AbstractMetaFunction *f : list) { + if (queryFunction(f, query)) + result.append(f); + } + return result; +} + +const AbstractMetaFunction *AbstractMetaClass::queryFirstFunction(const AbstractMetaFunctionList &list, + FunctionQueryOptions query) +{ + AbstractMetaFunctionList result; + for (AbstractMetaFunction *f : list) { + if (queryFunction(f, query)) + return f; } + return nullptr; +} - return functions; +AbstractMetaFunctionList AbstractMetaClass::queryFunctions(FunctionQueryOptions query) const +{ + return AbstractMetaClass::queryFunctionList(m_functions, query); } bool AbstractMetaClass::hasSignals() const { - return cppSignalFunctions().size() > 0; + return queryFirstFunction(m_functions, Signals | Visible | NotRemovedFromTargetLang) != nullptr; } +AbstractMetaFunctionList AbstractMetaClass::cppSignalFunctions() const +{ + return queryFunctions(Signals | Visible | NotRemovedFromTargetLang); +} /** * Adds the specified interface to this class by adding all the @@ -1932,13 +2078,15 @@ void AbstractMetaClass::setInterfaces(const AbstractMetaClassList &interfaces) } } +AbstractMetaField *AbstractMetaClass::findField(const QString &name) const +{ + return AbstractMetaField::find(m_fields, name); +} AbstractMetaEnum *AbstractMetaClass::findEnum(const QString &enumName) { - for (AbstractMetaEnum *e : qAsConst(m_enums)) { - if (e->name() == enumName) - return e; - } + if (AbstractMetaEnum *e = findByName(m_enums, enumName)) + return e; if (typeEntry()->designatedInterface()) return extractInterface()->findEnum(enumName); @@ -2002,8 +2150,8 @@ void AbstractMetaClass::fixFunctions() { if (m_functionsFixed) return; - else - m_functionsFixed = true; + + m_functionsFixed = true; AbstractMetaClass *superClass = baseClass(); AbstractMetaFunctionList funcs = functions(); @@ -2046,8 +2194,7 @@ void AbstractMetaClass::fixFunctions() // we generally don't care about private functions, but we have to get the ones that are // virtual in case they override abstract functions. bool add = (sf->isNormal() || sf->isSignal() || sf->isEmptyFunction()); - for (int fi = 0; fi < funcs.size(); ++fi) { - AbstractMetaFunction *f = funcs.at(fi); + for (AbstractMetaFunction *f : funcs) { if (f->isRemovedFromAllLanguages(f->implementingClass())) continue; @@ -2114,7 +2261,8 @@ void AbstractMetaClass::fixFunctions() if (mod.isNonFinal()) { hasNonFinalModifier = true; break; - } else if (mod.isPrivate()) { + } + if (mod.isPrivate()) { isBaseImplPrivate = true; break; } @@ -2222,6 +2370,8 @@ QString AbstractMetaType::formatSignature(bool minimal) const QString result; if (isConstant()) result += QLatin1String("const "); + if (isVolatile()) + result += QLatin1String("volatile "); if (isArray()) { // Build nested array dimensions a[2][3] in correct order result += m_arrayElementType->minimalSignature(); @@ -2245,10 +2395,10 @@ QString AbstractMetaType::formatSignature(bool minimal) const result += QLatin1String(" >"); } - if (!minimal && (m_indirections != 0 || m_referenceType != NoReference)) + if (!minimal && (!m_indirections.isEmpty() || m_referenceType != NoReference)) result += QLatin1Char(' '); - if (m_indirections) - result += QString(m_indirections, QLatin1Char('*')); + for (Indirection i : m_indirections) + result += TypeInfo::indirectionKeyword(i); switch (referenceType()) { case NoReference: break; @@ -2376,6 +2526,8 @@ QDebug operator<<(QDebug d, const AbstractMetaClass *ac) d << " [final]"; if (ac->m_baseClass) d << ", inherits \"" << ac->m_baseClass->name() << '"'; + if (ac->m_templateBaseClass) + d << ", inherits template \"" << ac->m_templateBaseClass->name() << '"'; const AbstractMetaEnumList &enums = ac->enums(); if (!enums.isEmpty()) d << ", enums[" << enums.size() << "]=" << enums; @@ -2406,6 +2558,18 @@ QDebug operator<<(QDebug d, const AbstractMetaClass *ac) } d << ')'; } + const auto &templateArguments = ac->templateArguments(); + if (const int count = templateArguments.size()) { + d << ", templateArguments=[" << count << "]("; + for (int i = 0; i < count; ++i) { + if (i) + d << ", "; + d << templateArguments.at(i); + } + d << ')'; + } + + } else { d << '0'; } diff --git a/sources/shiboken2/ApiExtractor/abstractmetalang.h b/sources/shiboken2/ApiExtractor/abstractmetalang.h index d1a0fbf88..aaefa32d5 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetalang.h +++ b/sources/shiboken2/ApiExtractor/abstractmetalang.h @@ -288,6 +288,7 @@ class AbstractMetaType { Q_GADGET public: + typedef QVector<Indirection> Indirections; enum TypeUsagePattern { InvalidPattern, @@ -436,6 +437,9 @@ public: m_constant = constant; } + bool isVolatile() const { return m_volatile; } + void setVolatile(bool v) { m_volatile = v; } + bool isConstRef() const; ReferenceType referenceType() const { return m_referenceType; } @@ -443,16 +447,21 @@ public: int actualIndirections() const { - return m_indirections + (m_referenceType == LValueReference ? 1 : 0); - } - int indirections() const - { - return m_indirections; + return m_indirections.size() + (m_referenceType == LValueReference ? 1 : 0); } + + Indirections indirectionsV() const { return m_indirections; } + void setIndirectionsV(const Indirections &i) { m_indirections = i; } + void clearIndirections() { m_indirections.clear(); } + + // "Legacy"? + int indirections() const { return m_indirections.size(); } void setIndirections(int indirections) { - m_indirections = indirections; + m_indirections = Indirections(indirections, Indirection::Pointer); } + void addIndirection(Indirection i = Indirection::Pointer) + { m_indirections.append(i); } void setArrayElementCount(int n) { @@ -527,6 +536,8 @@ public: bool hasTemplateChildren() const; + bool equals(const AbstractMetaType &rhs) const; + private: TypeUsagePattern determineUsagePattern() const; QString formatSignature(bool minimal) const; @@ -541,18 +552,25 @@ private: int m_arrayElementCount = -1; const AbstractMetaType *m_arrayElementType = nullptr; const AbstractMetaType *m_originalTemplateType = nullptr; + Indirections m_indirections; TypeUsagePattern m_pattern = InvalidPattern; uint m_constant : 1; + uint m_volatile : 1; uint m_cppInstantiation : 1; - int m_indirections : 4; - uint m_reserved : 26; // unused + uint m_reserved : 29; // unused + ReferenceType m_referenceType = NoReference; AbstractMetaTypeList m_children; Q_DISABLE_COPY(AbstractMetaType) }; +inline bool operator==(const AbstractMetaType &t1, const AbstractMetaType &t2) +{ return t1.equals(t2); } +inline bool operator!=(const AbstractMetaType &t1, const AbstractMetaType &t2) +{ return !t1.equals(t2); } + #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug d, const AbstractMetaType *at); #endif @@ -650,6 +668,13 @@ public: m_originalExpression = expr; } + bool hasDefaultValueExpression() const + { return !m_originalExpression.isEmpty() || !m_expression.isEmpty(); } + bool hasUnmodifiedDefaultValueExpression() const + { return !m_originalExpression.isEmpty() && m_originalExpression == m_expression; } + bool hasModifiedDefaultValueExpression() const + { return !m_expression.isEmpty() && m_originalExpression != m_expression; } + QString toString() const { return type()->name() + QLatin1Char(' ') + AbstractMetaVariable::name() + @@ -709,6 +734,9 @@ public: AbstractMetaField *copy() const; + static AbstractMetaField * + find(const AbstractMetaFieldList &haystack, const QString &needle); + private: mutable AbstractMetaFunction *m_getter = nullptr; mutable AbstractMetaFunction *m_setter = nullptr; @@ -816,13 +844,20 @@ public: return m_explicit; } - static bool isConversionOperator(QString funcName); + static bool isConversionOperator(const QString& funcName); + + ExceptionSpecification exceptionSpecification() const; + void setExceptionSpecification(ExceptionSpecification e); + + bool generateExceptionHandling() const { return m_generateExceptionHandling; } + void setGenerateExceptionHandling(bool g) { m_generateExceptionHandling = g; } + bool isConversionOperator() const { return isConversionOperator(originalName()); } - static bool isOperatorOverload(QString funcName); + static bool isOperatorOverload(const QString& funcName); bool isOperatorOverload() const { return isOperatorOverload(originalName()); @@ -1000,9 +1035,8 @@ public: // Returns the ownership rules for the given argument in the given context TypeSystem::Ownership ownership(const AbstractMetaClass *cls, TypeSystem::Language language, int idx) const; - bool isVirtualSlot() const; - QString typeReplaced(int argument_index) const; + bool isModifiedToArray(int argumentIndex) const; bool isRemovedFromAllLanguages(const AbstractMetaClass *) const; bool isRemovedFrom(const AbstractMetaClass *, TypeSystem::Language language) const; bool argumentRemoved(int) const; @@ -1057,11 +1091,16 @@ public: bool isCallOperator() const; + static AbstractMetaFunction * + find(const AbstractMetaFunctionList &haystack, const QString &needle); + #ifndef QT_NO_DEBUG_STREAM void formatDebugVerbose(QDebug &d) const; #endif private: + bool autoDetectAllowThread() const; + QString m_name; QString m_originalName; mutable QString m_cachedMinimalSignature; @@ -1082,6 +1121,9 @@ private: uint m_explicit : 1; uint m_pointerOperator : 1; uint m_isCallOperator : 1; + uint m_generateExceptionHandling: 1; + mutable int m_cachedAllowThread = -1; + ExceptionSpecification m_exceptionSpecification = ExceptionSpecification::Unknown; }; Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractMetaFunction::CompareResult) @@ -1246,7 +1288,8 @@ public: VirtualInCppFunctions = 0x0020000, // Only functions that are virtual in C++ VirtualInTargetLangFunctions = 0x0080000, // Only functions which are virtual in TargetLang NotRemovedFromTargetLang = 0x0400000, // Only functions that have not been removed from TargetLang - OperatorOverloads = 0x2000000 // Only functions that are operator overloads + OperatorOverloads = 0x2000000, // Only functions that are operator overloads + GenerateExceptionHandling = 0x4000000 }; Q_DECLARE_FLAGS(FunctionQueryOptions, FunctionQueryOption) Q_FLAG(FunctionQueryOption) @@ -1286,7 +1329,8 @@ public: bool hasSignal(const AbstractMetaFunction *f) const; bool hasConstructors() const; - bool hasCopyConstructor() const; + const AbstractMetaFunction *copyConstructor() const; + bool hasCopyConstructor() const { return copyConstructor() != nullptr; } bool hasPrivateCopyConstructor() const; void addDefaultConstructor(); @@ -1347,10 +1391,18 @@ public: return (hasNonPrivateConstructor() || !hasPrivateConstructor()) && !hasPrivateDestructor(); } + bool generateExceptionHandling() const; + AbstractMetaFunctionList queryFunctionsByName(const QString &name) const; + static bool queryFunction(const AbstractMetaFunction *f, FunctionQueryOptions query); + static AbstractMetaFunctionList queryFunctionList(const AbstractMetaFunctionList &list, + FunctionQueryOptions query); + static const AbstractMetaFunction *queryFirstFunction(const AbstractMetaFunctionList &list, + FunctionQueryOptions query); + AbstractMetaFunctionList queryFunctions(FunctionQueryOptions query) const; AbstractMetaFunctionList functionsInTargetLang() const; - inline AbstractMetaFunctionList cppSignalFunctions() const; + AbstractMetaFunctionList cppSignalFunctions() const; AbstractMetaFunctionList implicitConversions() const; /** @@ -1383,6 +1435,8 @@ public: m_fields << field; } + AbstractMetaField *findField(const QString &name) const; + AbstractMetaEnumList enums() const { return m_enums; @@ -1478,11 +1532,6 @@ public: m_forceShellClass = on; } - bool hasVirtualSlots() const - { - return m_hasVirtualSlots; - } - /** * Says if the class that declares or inherits a virtual function. * \return true if the class implements or inherits any virtual methods @@ -1650,6 +1699,8 @@ public: return m_hasToStringCapability; } + bool deleteInMainThread() const; + static AbstractMetaClass *findClass(const AbstractMetaClassList &classes, const QString &name); static AbstractMetaClass *findClass(const AbstractMetaClassList &classes, @@ -1666,7 +1717,6 @@ private: uint m_hasVirtuals : 1; uint m_isPolymorphic : 1; uint m_hasNonpublic : 1; - uint m_hasVirtualSlots : 1; uint m_hasNonPrivateConstructor : 1; uint m_hasPrivateConstructor : 1; uint m_functionsFixed : 1; @@ -1784,11 +1834,4 @@ private: int m_index = -1; }; -inline AbstractMetaFunctionList AbstractMetaClass::cppSignalFunctions() const -{ - return queryFunctions(Signals - | Visible - | NotRemovedFromTargetLang); -} - #endif // ABSTRACTMETALANG_H diff --git a/sources/shiboken2/ApiExtractor/apiextractor.cpp b/sources/shiboken2/ApiExtractor/apiextractor.cpp index 171011cd4..775485c81 100644 --- a/sources/shiboken2/ApiExtractor/apiextractor.cpp +++ b/sources/shiboken2/ApiExtractor/apiextractor.cpp @@ -177,41 +177,9 @@ static const AbstractMetaEnum* findEnumOnClasses(AbstractMetaClassList metaClass return result; } -const AbstractMetaEnum* ApiExtractor::findAbstractMetaEnum(const EnumTypeEntry* typeEntry) const -{ - if (!typeEntry) - return 0; - const AbstractMetaEnumList &globalEnums = m_builder->globalEnums(); - for (AbstractMetaEnum* metaEnum : globalEnums) { - if (metaEnum->typeEntry() == typeEntry) - return metaEnum; - } - return findEnumOnClasses(m_builder->classes(), typeEntry); -} - const AbstractMetaEnum* ApiExtractor::findAbstractMetaEnum(const TypeEntry* typeEntry) const { - if (!typeEntry) - return 0; - if (typeEntry->isFlags()) - return findAbstractMetaEnum(reinterpret_cast<const FlagsTypeEntry*>(typeEntry)); - if (typeEntry->isEnum()) - return findAbstractMetaEnum(reinterpret_cast<const EnumTypeEntry*>(typeEntry)); - return 0; -} - -const AbstractMetaEnum* ApiExtractor::findAbstractMetaEnum(const FlagsTypeEntry* typeEntry) const -{ - if (!typeEntry) - return 0; - return findAbstractMetaEnum(typeEntry->originator()); -} - -const AbstractMetaEnum* ApiExtractor::findAbstractMetaEnum(const AbstractMetaType* metaType) const -{ - if (!metaType) - return 0; - return findAbstractMetaEnum(metaType->typeEntry()); + return m_builder->findEnum(typeEntry); } int ApiExtractor::classCount() const diff --git a/sources/shiboken2/ApiExtractor/apiextractor.h b/sources/shiboken2/ApiExtractor/apiextractor.h index 674e5a742..ab520c9de 100644 --- a/sources/shiboken2/ApiExtractor/apiextractor.h +++ b/sources/shiboken2/ApiExtractor/apiextractor.h @@ -87,10 +87,7 @@ public: PrimitiveTypeEntryList primitiveTypes() const; ContainerTypeEntryList containerTypes() const; - const AbstractMetaEnum* findAbstractMetaEnum(const EnumTypeEntry* typeEntry) const; const AbstractMetaEnum* findAbstractMetaEnum(const TypeEntry* typeEntry) const; - const AbstractMetaEnum* findAbstractMetaEnum(const FlagsTypeEntry* typeEntry) const; - const AbstractMetaEnum* findAbstractMetaEnum(const AbstractMetaType* metaType) const; int classCount() const; diff --git a/sources/shiboken2/ApiExtractor/clangparser/clangbuilder.cpp b/sources/shiboken2/ApiExtractor/clangparser/clangbuilder.cpp index af7f96068..40f915028 100644 --- a/sources/shiboken2/ApiExtractor/clangparser/clangbuilder.cpp +++ b/sources/shiboken2/ApiExtractor/clangparser/clangbuilder.cpp @@ -146,6 +146,7 @@ class BuilderPrivate { public: typedef QHash<CXCursor, ClassModelItem> CursorClassHash; typedef QHash<CXCursor, TypeDefModelItem> CursorTypedefHash; + typedef QHash<CXType, TypeInfo> TypeInfoHash; explicit BuilderPrivate(BaseVisitor *bv) : m_baseVisitor(bv), m_model(new CodeModel) { @@ -180,9 +181,16 @@ public: CodeModel::FunctionType t = CodeModel::Normal) const; FunctionModelItem createMemberFunction(const CXCursor &cursor) const; void qualifyConstructor(const CXCursor &cursor); + TypeInfo createTypeInfoHelper(const CXType &type) const; // uncashed TypeInfo createTypeInfo(const CXType &type) const; TypeInfo createTypeInfo(const CXCursor &cursor) const { return createTypeInfo(clang_getCursorType(cursor)); } + void addTemplateInstantiations(const CXType &type, + QString *typeName, + TypeInfo *t) const; + bool addTemplateInstantiationsRecursion(const CXType &type, TypeInfo *t) const; + + void addTypeDef(const CXCursor &cursor, const TypeInfo &ti); TemplateParameterModelItem createTemplateParameter(const CXCursor &cursor) const; TemplateParameterModelItem createNonTypeTemplateParameter(const CXCursor &cursor) const; @@ -205,6 +213,8 @@ public: CursorClassHash m_cursorClassHash; CursorTypedefHash m_cursorTypedefHash; + mutable TypeInfoHash m_typeInfoHash; // Cache type information + ClassModelItem m_currentClass; EnumModelItem m_currentEnum; FunctionModelItem m_currentFunction; @@ -247,6 +257,25 @@ bool BuilderPrivate::addClass(const CXCursor &cursor, CodeModel::ClassType t) return true; } +static inline ExceptionSpecification exceptionSpecificationFromClang(int ce) +{ + switch (ce) { + case CXCursor_ExceptionSpecificationKind_BasicNoexcept: + case CXCursor_ExceptionSpecificationKind_ComputedNoexcept: + case CXCursor_ExceptionSpecificationKind_DynamicNone: // throw() + return ExceptionSpecification::NoExcept; + case CXCursor_ExceptionSpecificationKind_Dynamic: // throw(t1..) + case CXCursor_ExceptionSpecificationKind_MSAny: // throw(...) + return ExceptionSpecification::Throws; + default: + // CXCursor_ExceptionSpecificationKind_None, + // CXCursor_ExceptionSpecificationKind_Unevaluated, + // CXCursor_ExceptionSpecificationKind_Uninstantiated + break; + } + return ExceptionSpecification::Unknown; +} + FunctionModelItem BuilderPrivate::createFunction(const CXCursor &cursor, CodeModel::FunctionType t) const { @@ -256,10 +285,11 @@ FunctionModelItem BuilderPrivate::createFunction(const CXCursor &cursor, name = fixTypeName(name); FunctionModelItem result(new _FunctionModelItem(m_model, name)); setFileName(cursor, result.data()); - result->setType(createTypeInfo(clang_getCursorResultType(cursor))); + result->setType(createTypeInfoHelper(clang_getCursorResultType(cursor))); result->setFunctionType(t); result->setScope(m_scope); result->setStatic(clang_Cursor_getStorageClass(cursor) == CX_SC_Static); + result->setExceptionSpecification(exceptionSpecificationFromClang(clang_getCursorExceptionSpecificationType(cursor))); switch (clang_getCursorAvailability(cursor)) { case CXAvailability_Available: break; @@ -335,7 +365,7 @@ TemplateParameterModelItem BuilderPrivate::createTemplateParameter(const CXCurso TemplateParameterModelItem BuilderPrivate::createNonTypeTemplateParameter(const CXCursor &cursor) const { TemplateParameterModelItem result = createTemplateParameter(cursor); - result->setType(createTypeInfo(cursor)); + result->setType(createTypeInfoHelper(clang_getCursorType(cursor))); return result; } @@ -359,38 +389,6 @@ struct ArrayDimensionResult int position; }; -static ArrayDimensionResult arrayDimensions(const QString &typeName) -{ - ArrayDimensionResult result; - result.position = typeName.indexOf(QLatin1Char('[')); - for (int openingPos = result.position; openingPos != -1; ) { - const int closingPos = typeName.indexOf(QLatin1Char(']'), openingPos + 1); - if (closingPos == -1) - break; - result.dimensions.append(typeName.midRef(openingPos + 1, closingPos - openingPos - 1)); - openingPos = typeName.indexOf(QLatin1Char('['), closingPos + 1); - } - return result; -} - -// Array helpers: Parse "a[2][4]" into a list of dimensions or "" for none -static QStringList parseArrayArgs(const CXType &type, QString *typeName) -{ - const ArrayDimensionResult dimensions = arrayDimensions(*typeName); - Q_ASSERT(!dimensions.dimensions.isEmpty()); - - QStringList result; - // get first dimension from clang, preferably. - // "a[]" is seen as pointer by Clang, set special indicator "" - const long long size = clang_getArraySize(type); - result.append(size >= 0 ? QString::number(size) : QString()); - // Parse out remaining dimensions - for (int i = 1, count = dimensions.dimensions.size(); i < count; ++i) - result.append(dimensions.dimensions.at(i).toString()); - typeName->truncate(dimensions.position); - return result; -} - // Create qualified name "std::list<std::string>" -> ("std", "list<std::string>") static QStringList qualifiedName(const QString &t) { @@ -412,71 +410,143 @@ static QStringList qualifiedName(const QString &t) return result; } -TypeInfo BuilderPrivate::createTypeInfo(const CXType &type) const +static bool isArrayType(CXTypeKind k) +{ + return k == CXType_ConstantArray || k == CXType_IncompleteArray + || k == CXType_VariableArray || k == CXType_DependentSizedArray; +} + +static bool isPointerType(CXTypeKind k) +{ + return k == CXType_Pointer || k == CXType_LValueReference || k == CXType_RValueReference; +} + +bool BuilderPrivate::addTemplateInstantiationsRecursion(const CXType &type, TypeInfo *t) const +{ + // Template arguments + switch (type.kind) { + case CXType_Elaborated: + case CXType_Record: + case CXType_Unexposed: + if (const int numTemplateArguments = qMax(0, clang_Type_getNumTemplateArguments(type))) { + for (unsigned tpl = 0; tpl < unsigned(numTemplateArguments); ++tpl) { + const CXType argType = clang_Type_getTemplateArgumentAsType(type, tpl); + // CXType_Invalid is returned when hitting on a specialization + // of a non-type template (template <int v>). + if (argType.kind == CXType_Invalid) + return false; + t->addInstantiation(createTypeInfoHelper(argType)); + } + } + break; + default: + break; + } + return true; +} + +static void dummyTemplateArgumentHandler(int, const QStringRef &) {} + +void BuilderPrivate::addTemplateInstantiations(const CXType &type, + QString *typeName, + TypeInfo *t) const +{ + // In most cases, for templates like "Vector<A>", Clang will give us the + // arguments by recursing down the type. However this will fail for example + // within template classes (for functions like the copy constructor): + // template <class T> + // class Vector { + // Vector(const Vector&); + // }; + // In that case, have TypeInfo parse the list from the spelling. + // Finally, remove the list "<>" from the type name. + const bool parsed = addTemplateInstantiationsRecursion(type, t) + && !t->instantiations().isEmpty(); + const QPair<int, int> pos = parsed + ? parseTemplateArgumentList(*typeName, dummyTemplateArgumentHandler) + : t->parseTemplateArgumentList(*typeName); + if (pos.first != -1 && pos.second != -1 && pos.second > pos.first) + typeName->remove(pos.first, pos.second - pos.first); +} + +TypeInfo BuilderPrivate::createTypeInfoHelper(const CXType &type) const { if (type.kind == CXType_Pointer) { // Check for function pointers, first. const CXType pointeeType = clang_getPointeeType(type); const int argCount = clang_getNumArgTypes(pointeeType); if (argCount >= 0) { - TypeInfo result = createTypeInfo(clang_getResultType(pointeeType)); + TypeInfo result = createTypeInfoHelper(clang_getResultType(pointeeType)); result.setFunctionPointer(true); for (int a = 0; a < argCount; ++a) - result.addArgument(createTypeInfo(clang_getArgType(pointeeType, unsigned(a)))); + result.addArgument(createTypeInfoHelper(clang_getArgType(pointeeType, unsigned(a)))); return result; } } TypeInfo typeInfo; - QString typeName = fixTypeName(getTypeName(type)); - int indirections = 0; - // "int **" - for ( ; typeName.endsWith(QLatin1Char('*')) ; ++indirections) - typeName.chop(1); - typeInfo.setIndirections(indirections); - // "int &&" - if (typeName.endsWith(QLatin1String("&&"))) { - typeName.chop(2); - typeInfo.setReferenceType(RValueReference); - } else if (typeName.endsWith(QLatin1Char('&'))) { // "int &" - typeName.chop(1); - typeInfo.setReferenceType(LValueReference); + CXType nestedType = type; + for (; isArrayType(nestedType.kind); nestedType = clang_getArrayElementType(nestedType)) { + const long long size = clang_getArraySize(nestedType); + typeInfo.addArrayElement(size >= 0 ? QString::number(size) : QString()); } - // "int [3], int[]" - if (type.kind == CXType_ConstantArray || type.kind == CXType_IncompleteArray - || type.kind == CXType_VariableArray || type.kind == CXType_DependentSizedArray) { - typeInfo.setArrayElements(parseArrayArgs(type, &typeName)); + TypeInfo::Indirections indirections; + for (; isPointerType(nestedType.kind); nestedType = clang_getPointeeType(nestedType)) { + switch (nestedType.kind) { + case CXType_Pointer: + indirections.prepend(clang_isConstQualifiedType(nestedType) != 0 + ? Indirection::ConstPointer : Indirection::Pointer); + break; + case CXType_LValueReference: + typeInfo.setReferenceType(LValueReference); + break; + case CXType_RValueReference: + typeInfo.setReferenceType(RValueReference); + break; + default: + break; + } } + typeInfo.setIndirectionsV(indirections); - bool isConstant = clang_isConstQualifiedType(type) != 0; - // A "char *const" parameter, is considered to be const-qualified by Clang, but - // not in the TypeInfo sense (corresponds to "char *" and not "const char *"). - if (type.kind == CXType_Pointer && isConstant && typeName.endsWith(QLatin1String("const"))) { - typeName.chop(5); - typeName = typeName.trimmed(); - isConstant = false; - } - // Clang has been observed to return false for "const int .." - if (!isConstant && typeName.startsWith(QLatin1String("const "))) { - typeName.remove(0, 6); - isConstant = true; - } - typeInfo.setConstant(isConstant); + typeInfo.setConstant(clang_isConstQualifiedType(nestedType) != 0); + typeInfo.setVolatile(clang_isVolatileQualifiedType(nestedType) != 0); - // clang_isVolatileQualifiedType() returns true for "volatile int", but not for "volatile int *" - if (typeName.startsWith(QLatin1String("volatile "))) { - typeName.remove(0, 9); - typeInfo.setVolatile(true); + QString typeName = getTypeName(nestedType); + while (TypeInfo::stripLeadingConst(&typeName) + || TypeInfo::stripLeadingVolatile(&typeName)) { } - typeName = typeName.trimmed(); + // Obtain template instantiations if the name has '<' (thus excluding + // typedefs like "std::string". + if (typeName.contains(QLatin1Char('<'))) + addTemplateInstantiations(nestedType, &typeName, &typeInfo); typeInfo.setQualifiedName(qualifiedName(typeName)); // 3320:CINDEX_LINKAGE int clang_getNumArgTypes(CXType T); function ptr types? + typeInfo.simplifyStdType(); return typeInfo; } +TypeInfo BuilderPrivate::createTypeInfo(const CXType &type) const +{ + TypeInfoHash::iterator it = m_typeInfoHash.find(type); + if (it == m_typeInfoHash.end()) + it = m_typeInfoHash.insert(type, createTypeInfoHelper(type)); + return it.value(); +} + +void BuilderPrivate::addTypeDef(const CXCursor &cursor, const TypeInfo &ti) +{ + TypeDefModelItem item(new _TypeDefModelItem(m_model, getCursorSpelling(cursor))); + setFileName(cursor, item.data()); + item->setType(ti); + item->setScope(m_scope); + m_scopeStack.back()->addTypeDef(item); + m_cursorTypedefHash.insert(cursor, item); +} + // extract an expression from the cursor via source // CXCursor_EnumConstantDecl, ParmDecl (a = Flag1 | Flag2) QString BuilderPrivate::cursorValueExpression(BaseVisitor *bv, const CXCursor &cursor) const @@ -795,9 +865,8 @@ BaseVisitor::StartTokenResult Builder::startToken(const CXCursor &cursor) d->m_currentFunction = d->createMemberFunction(cursor); d->m_scopeStack.back()->addFunction(d->m_currentFunction); break; - } else { - return Skip; // inline member functions outside class } + return Skip; // inline member functions outside class } } Q_FALLTHROUGH(); // fall through to free template function. @@ -868,17 +937,14 @@ BaseVisitor::StartTokenResult Builder::startToken(const CXCursor &cursor) } break; case CXCursor_TypeAliasDecl: - case CXCursor_TypeAliasTemplateDecl: // May contain nested CXCursor_TemplateTypeParameter - return Skip; - case CXCursor_TypedefDecl: { - const QString name = getCursorSpelling(cursor); - TypeDefModelItem item(new _TypeDefModelItem(d->m_model, name)); - setFileName(cursor, item.data()); - item->setType(d->createTypeInfo(clang_getTypedefDeclUnderlyingType(cursor))); - item->setScope(d->m_scope); - d->m_scopeStack.back()->addTypeDef(item); - d->m_cursorTypedefHash.insert(cursor, item); + case CXCursor_TypeAliasTemplateDecl: { // May contain nested CXCursor_TemplateTypeParameter + const CXType type = clang_getCanonicalType(clang_getCursorType(cursor)); + if (type.kind > CXType_Unexposed) + d->addTypeDef(cursor, d->createTypeInfo(type)); } + return Skip; + case CXCursor_TypedefDecl: + d->addTypeDef(cursor, d->createTypeInfo(clang_getTypedefDeclUnderlyingType(cursor))); break; case CXCursor_TypeRef: if (!d->m_currentFunction.isNull()) { diff --git a/sources/shiboken2/ApiExtractor/clangparser/clangparser.cpp b/sources/shiboken2/ApiExtractor/clangparser/clangparser.cpp index ce0b6554d..e116f8b83 100644 --- a/sources/shiboken2/ApiExtractor/clangparser/clangparser.cpp +++ b/sources/shiboken2/ApiExtractor/clangparser/clangparser.cpp @@ -166,7 +166,7 @@ static inline const char **byteArrayListToFlatArgV(const QByteArrayList &bl) return result; } -static QByteArray msgCreateTranslationUnit(const QByteArrayList clangArgs, unsigned flags) +static QByteArray msgCreateTranslationUnit(const QByteArrayList &clangArgs, unsigned flags) { QByteArray result = "clang_parseTranslationUnit2(0x"; result += QByteArray::number(flags, 16); diff --git a/sources/shiboken2/ApiExtractor/clangparser/clangutils.cpp b/sources/shiboken2/ApiExtractor/clangparser/clangutils.cpp index 2ff18b23b..8bee28cdf 100644 --- a/sources/shiboken2/ApiExtractor/clangparser/clangutils.cpp +++ b/sources/shiboken2/ApiExtractor/clangparser/clangutils.cpp @@ -46,6 +46,18 @@ uint qHash(const CXCursor &c, uint seed) ^ qHash(c.data[1]) ^ qHash(c.data[2]) ^ seed; } +bool operator==(const CXType &t1, const CXType &t2) +{ + return t1.kind == t2.kind && t1.data[0] == t2.data[0] + && t1.data[1] == t2.data[1]; +} + +uint qHash(const CXType &ct, uint seed) +{ + return uint(ct.kind) ^ uint(0xFFFFFFFF & quintptr(ct.data[0])) + ^ uint(0xFFFFFFFF & quintptr(ct.data[1])) ^ seed; +} + namespace clang { SourceLocation getExpansionLocation(const CXSourceLocation &location) @@ -160,6 +172,43 @@ QVector<Diagnostic> getDiagnostics(CXTranslationUnit tu) return result; } +QPair<int, int> parseTemplateArgumentList(const QString &l, + const TemplateArgumentHandler &handler, + int from) +{ + const int ltPos = l.indexOf(QLatin1Char('<'), from); + if (ltPos == - 1) + return qMakePair(-1, -1); + int startPos = ltPos + 1; + int level = 1; + for (int p = startPos, end = l.size(); p < end; ) { + const char c = l.at(p).toLatin1(); + switch (c) { + case ',': + case '>': + handler(level, l.midRef(startPos, p - startPos).trimmed()); + ++p; + if (c == '>') { + if (--level == 0) + return qMakePair(ltPos, p); + // Skip over next ',': "a<b<c,d>,e>" + for (; p < end && (l.at(p).isSpace() || l.at(p) == QLatin1Char(',')); ++p) {} + } + startPos = p; + break; + case '<': + handler(level, l.midRef(startPos, p - startPos).trimmed()); + ++level; + startPos = ++p; + break; + default: + ++p; + break; + } + } + return qMakePair(-1, -1); +} + CXDiagnosticSeverity maxSeverity(const QVector<Diagnostic> &ds) { CXDiagnosticSeverity result = CXDiagnostic_Ignored; diff --git a/sources/shiboken2/ApiExtractor/clangparser/clangutils.h b/sources/shiboken2/ApiExtractor/clangparser/clangutils.h index 98d0c9752..b290aac9a 100644 --- a/sources/shiboken2/ApiExtractor/clangparser/clangutils.h +++ b/sources/shiboken2/ApiExtractor/clangparser/clangutils.h @@ -34,11 +34,16 @@ #include <QtCore/QString> #include <QtCore/QVector> +#include <functional> + QT_FORWARD_DECLARE_CLASS(QDebug) bool operator==(const CXCursor &c1, const CXCursor &c2); uint qHash(const CXCursor &c, uint seed = 0); +bool operator==(const CXType &t1, const CXType &t2); +uint qHash(const CXType &ct, uint seed); + namespace clang { QString getCursorKindName(CXCursorKind cursorKind); @@ -92,6 +97,14 @@ struct Diagnostic { QVector<Diagnostic> getDiagnostics(CXTranslationUnit tu); CXDiagnosticSeverity maxSeverity(const QVector<Diagnostic> &ds); +// Parse a template argument list "a<b<c,d>,e>" and invoke a handler +// with each match (level and string). Return begin and end of the list. +typedef std::function<void(int /*level*/, const QStringRef &)> TemplateArgumentHandler; + +QPair<int, int> parseTemplateArgumentList(const QString &l, + const TemplateArgumentHandler &handler, + int from = 0); + #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug, const SourceLocation &); QDebug operator<<(QDebug, const Diagnostic &); diff --git a/sources/shiboken2/ApiExtractor/doc/conf.py.in b/sources/shiboken2/ApiExtractor/doc/conf.py.in index 7251aaccd..609c4a363 100644 --- a/sources/shiboken2/ApiExtractor/doc/conf.py.in +++ b/sources/shiboken2/ApiExtractor/doc/conf.py.in @@ -132,10 +132,6 @@ html_theme_path = ['@CMAKE_CURRENT_SOURCE_DIR@/_themes'] # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -html_use_smartypants = True - # Custom sidebar templates, maps document names to template names. #html_sidebars = { '' : ''} diff --git a/sources/shiboken2/ApiExtractor/doc/typesystem_manipulating_objects.rst b/sources/shiboken2/ApiExtractor/doc/typesystem_manipulating_objects.rst index ff6ea5317..12b866ad7 100644 --- a/sources/shiboken2/ApiExtractor/doc/typesystem_manipulating_objects.rst +++ b/sources/shiboken2/ApiExtractor/doc/typesystem_manipulating_objects.rst @@ -11,6 +11,9 @@ inject-code given type or function, and it is a child of the :ref:`object-type`, :ref:`value-type`, :ref:`modify-function` and :ref:`add-function` nodes. + The code can be embedded into XML (be careful to use the correct XML entities + for characters like '<', '>', '&'): + .. code-block:: xml <value-type> @@ -20,6 +23,18 @@ inject-code </inject-code> </value-type> + or obtained from an external file: + + .. code-block:: xml + + <value-type> + <inject-code class="native | target | target-declaration" + position="beginning | end" since="..." + file="external_source.cpp" + snippet="label"/> + </value-type> + + The ``class`` attribute specifies which module of the generated code that will be affected by the code injection. The ``class`` attribute accepts the following values: @@ -28,6 +43,8 @@ inject-code * target: The binding code * target-declaration: The code will be injected into the generated header file containing the c++ wrapper class definition. + * file: The file name + * snippet: The snippet label (optional) If the ``position`` attribute is set to *beginning* (the default), the code is inserted at the beginning of the function. If it is set to *end*, the code @@ -35,6 +52,16 @@ inject-code The ``since`` attribute specify the API version where this code was injected. + If a ``snippet`` label is given, the code between annotations of the form + + .. code-block:: c++ + + // @snippet label + ... + // @snippet label + + will be extracted. + modify-field ^^^^^^^^^^^^ @@ -74,6 +101,8 @@ modify-function since="..." remove="all | c++" access="public | private | protected" + allow-thread="true | auto | false" + exception-handling="off | auto-off | auto-on | on" rename="..." /> </object-type> @@ -82,6 +111,26 @@ modify-function The ``since`` attribute specify the API version when this function was modified. + The ``allow-thread`` attribute specifies whether a function should be wrapped + into ``Py_BEGIN_ALLOW_THREADS`` and ``Py_END_ALLOW_THREADS``, that is, + temporarily release the GIL (global interpreter lock). Doing so is required + for any thread-related function (wait operations), functions that might call + a virtual function (potentially reimplemented in Python), and recommended for + lengthy I/O operations or similar. It has performance costs, though. + The value ``auto`` means that it will be turned off for functions for which + it is deemed to be safe, for example, simple getters. + + The ``exception-handling`` attribute specifies whether to generate exception + handling code (nest the function call into try / catch statements). It accepts + the following values: + + * no, false: Do not generate exception handling code + * auto-off: Generate exception handling code for functions + declaring a non-empty ``throw`` list + * auto-on: Generate exception handling code unless function + declares ``noexcept`` + * yes, true: Always generate exception handling code + The ``remove``, ``access`` and ``rename`` attributes are *optional* attributes for added convenience; they serve the same purpose as the deprecated tags :ref:`remove`, :ref:`access` and :ref:`rename`. @@ -130,3 +179,4 @@ conversion-rule .. note:: You can also use the conversion-rule node to specify :ref:`how the conversion of a single function argument should be done in a function <conversion-rule>`. + The ``file`` and ``snippet`` attributes are also supported (see :ref:`inject-code` nodes). diff --git a/sources/shiboken2/ApiExtractor/doc/typesystem_specifying_types.rst b/sources/shiboken2/ApiExtractor/doc/typesystem_specifying_types.rst index 322f9bca6..c3180ae88 100644 --- a/sources/shiboken2/ApiExtractor/doc/typesystem_specifying_types.rst +++ b/sources/shiboken2/ApiExtractor/doc/typesystem_specifying_types.rst @@ -11,7 +11,7 @@ typesystem .. code-block:: xml - <typesystem package="..." default-superclass="..."> + <typesystem package="..." default-superclass="..." exception-handling="..."> </typesystem> The **package** attribute is a string describing the package to be used, @@ -19,6 +19,9 @@ typesystem The *optional* **default-superclass** attribute is the canonical C++ base class name of all objects, e.g., "object". + The *optional* **exception-handling** attribute specifies the default exception + handling mode of all objects (see :ref:`modify-function`). + load-typesystem ^^^^^^^^^^^^^^^ @@ -216,6 +219,7 @@ value-type <typesystem> <value-type name="..." since="..." copyable="yes | no" + exception-handling="..." hash-function="..." stream="yes | no" default-constructor="..." @@ -243,6 +247,9 @@ value-type The **revision** attribute can be used to specify a revision for each type, easing the production of ABI compatible bindings. + The *optional* **exception-handling** attribute specifies the default exception + handling mode of all functions (see :ref:`modify-function`). + .. _object-type: object-type @@ -258,6 +265,7 @@ object-type <object-type name="..." since="..." copyable="yes | no" + exception-handling="..." hash-function="..." stream="yes | no" revision="..." /> @@ -278,6 +286,9 @@ object-type The **revision** attribute can be used to specify a revision for each type, easing the production of ABI compatible bindings. + The *optional* **exception-handling** attribute specifies the default exception + handling mode of all functions (see :ref:`modify-function`). + interface-type ^^^^^^^^^^^^^^ @@ -329,6 +340,38 @@ container-type The *optional* **since** value is used to specify the API version of this container. +typedef-type +^^^^^^^^^^^^ + + The typedef-type allows for specifying typedefs in the typesystem. They + are mostly equivalent to spelling out the typedef in the included header, which + is often complicated when trying to wrap libraries whose source code cannot be + easily extended. + + .. code-block:: xml + + <typesystem> + <typedef-type name="..." + source="..." + since="..." + </typesystem> + + The **source** attribute is the source. Example: + + .. code-block:: xml + + <namespace-type name='std'> + <value-type name='optional' generate='no'/>\n" + </namespace-type> + <typedef-type name="IntOptional" source="std::optional<int>"/> + + is equivalent to + + .. code-block:: c++ + + typedef std::optional<int> IntOptional; + + The *optional* **since** value is used to specify the API version of this type. .. _custom-type: @@ -348,6 +391,26 @@ custom-type The **name** attribute is the name of the custom type, e.g., "PyObject". +.. _smart-pointer-type: + +smart-pointer-type +^^^^^^^^^^^^^^^^^^ + + The smart pointer type node indicates that the given class is a smart pointer + and requires inserting calls to **getter** to access the pointeee. + Currently, only the **type** *shared* is supported and the usage is limited + to function return values. + **ref-count-method** specifies the name of the method used to do reference counting. + + .. code-block:: xml + + <typesystem> + <smart-pointer-type name="..." + since="..." + type="..." + getter="..." + ref-count-method="..."/> + </typesystem> .. _function: diff --git a/sources/shiboken2/ApiExtractor/docparser.cpp b/sources/shiboken2/ApiExtractor/docparser.cpp index 9305332ba..99921e7d3 100644 --- a/sources/shiboken2/ApiExtractor/docparser.cpp +++ b/sources/shiboken2/ApiExtractor/docparser.cpp @@ -27,6 +27,7 @@ ****************************************************************************/ #include "docparser.h" #include "abstractmetalang.h" +#include "messages.h" #include "reporthandler.h" #include "typesystem.h" #include <QtCore/QDebug> @@ -50,9 +51,7 @@ DocParser::DocParser() #endif } -DocParser::~DocParser() -{ -} +DocParser::~DocParser() = default; QString DocParser::getDocumentation(QXmlQuery& xquery, const QString& query, const DocModificationList& mods) const @@ -109,58 +108,21 @@ AbstractMetaFunctionList DocParser::documentableFunctions(const AbstractMetaClas return result; } -QString DocParser::msgCannotFindDocumentation(const QString &fileName, - const char *what, const QString &name, - const QString &query) -{ - QString result; - QTextStream(&result) << "Cannot find documentation for " << what - << ' ' << name << " in:\n " << QDir::toNativeSeparators(fileName) - << "\n using query:\n " << query; - return result; -} - -QString DocParser::msgCannotFindDocumentation(const QString &fileName, - const AbstractMetaClass *metaClass, - const AbstractMetaFunction *function, - const QString &query) -{ - const QString name = metaClass->name() + QLatin1String("::") - + function->minimalSignature(); - return msgCannotFindDocumentation(fileName, "function", name, query); -} - -QString DocParser::msgCannotFindDocumentation(const QString &fileName, - const AbstractMetaClass *metaClass, - const AbstractMetaEnum *e, - const QString &query) -{ - return msgCannotFindDocumentation(fileName, "enum", - metaClass->name() + QLatin1String("::") + e->name(), - query); -} - -QString DocParser::msgCannotFindDocumentation(const QString &fileName, - const AbstractMetaClass *metaClass, - const AbstractMetaField *f, - const QString &query) -{ - return msgCannotFindDocumentation(fileName, "field", - metaClass->name() + QLatin1String("::") + f->name(), - query); -} #ifdef HAVE_LIBXSLT namespace { -struct XslResources +class XslResources { - xmlDocPtr xmlDoc; - xsltStylesheetPtr xslt; - xmlDocPtr xslResult; + Q_DISABLE_COPY(XslResources) + +public: + xmlDocPtr xmlDoc = nullptr; + xsltStylesheetPtr xslt = nullptr; + xmlDocPtr xslResult = nullptr; - XslResources() : xmlDoc(0), xslt(0), xslResult(0) {} + XslResources() = default; ~XslResources() { @@ -186,27 +148,6 @@ static inline bool isXpathDocModification(const DocModification &mod) return mod.mode() == TypeSystem::DocModificationXPathReplace; } -QString msgXpathDocModificationError(const DocModificationList& mods, - const QString &what) -{ - QString result; - QTextStream str(&result); - str << "Error when applying modifications ("; - for (const DocModification &mod : mods) { - if (isXpathDocModification(mod)) { - str << '"' << mod.xpath() << "\" -> \""; - const QString simplified = mod.code().simplified(); - if (simplified.size() > 20) - str << simplified.leftRef(20) << "..."; - else - str << simplified; - str << '"'; - } - } - str << "): " << what; - return result; -} - QString DocParser::applyDocModifications(const DocModificationList& mods, const QString& xml) const { if (mods.isEmpty() || xml.isEmpty() diff --git a/sources/shiboken2/ApiExtractor/docparser.h b/sources/shiboken2/ApiExtractor/docparser.h index fff71a877..7cbbef28e 100644 --- a/sources/shiboken2/ApiExtractor/docparser.h +++ b/sources/shiboken2/ApiExtractor/docparser.h @@ -120,22 +120,6 @@ protected: static AbstractMetaFunctionList documentableFunctions(const AbstractMetaClass *metaClass); - static QString msgCannotFindDocumentation(const QString &fileName, - const char *what, const QString &name, - const QString &query); - static QString msgCannotFindDocumentation(const QString &fileName, - const AbstractMetaClass *metaClass, - const AbstractMetaFunction *function, - const QString &query); - static QString msgCannotFindDocumentation(const QString &fileName, - const AbstractMetaClass *metaClass, - const AbstractMetaEnum *e, - const QString &query); - static QString msgCannotFindDocumentation(const QString &fileName, - const AbstractMetaClass *metaClass, - const AbstractMetaField *f, - const QString &query); - private: QString m_packageName; QString m_docDataDir; diff --git a/sources/shiboken2/ApiExtractor/doxygenparser.cpp b/sources/shiboken2/ApiExtractor/doxygenparser.cpp index dfdb37a47..e238aa1f7 100644 --- a/sources/shiboken2/ApiExtractor/doxygenparser.cpp +++ b/sources/shiboken2/ApiExtractor/doxygenparser.cpp @@ -28,6 +28,7 @@ #include "doxygenparser.h" #include "abstractmetalang.h" +#include "messages.h" #include "reporthandler.h" #include "typesystem.h" @@ -35,23 +36,17 @@ #include <QtCore/QFile> #include <QtCore/QDir> -namespace +static QString getSectionKindAttr(const AbstractMetaFunction *func) { - -QString getSectionKindAttr(const AbstractMetaFunction* func) -{ - if (func->isSignal()) { + if (func->isSignal()) return QLatin1String("signal"); - } else { - QString kind = func->isPublic() ? QLatin1String("public") : QLatin1String("protected"); - if (func->isStatic()) - kind += QLatin1String("-static"); - else if (func->isSlot()) - kind += QLatin1String("-slot"); - return kind; - } -} - + QString kind = func->isPublic() + ? QLatin1String("public") : QLatin1String("protected"); + if (func->isStatic()) + kind += QLatin1String("-static"); + else if (func->isSlot()) + kind += QLatin1String("-slot"); + return kind; } Documentation DoxygenParser::retrieveModuleDocumentation() @@ -73,13 +68,12 @@ void DoxygenParser::fillDocumentation(AbstractMetaClass* metaClass) doxyFileSuffix += QLatin1String(".xml"); const char* prefixes[] = { "class", "struct", "namespace" }; - const int numPrefixes = sizeof(prefixes) / sizeof(const char*); bool isProperty = false; QString doxyFilePath; - for (int i = 0; i < numPrefixes; ++i) { + for (const char *prefix : prefixes) { doxyFilePath = documentationDataDirectory() + QLatin1Char('/') - + QLatin1String(prefixes[i]) + doxyFileSuffix; + + QLatin1String(prefix) + doxyFileSuffix; if (QFile::exists(doxyFilePath)) break; doxyFilePath.clear(); diff --git a/sources/shiboken2/ApiExtractor/fileout.cpp b/sources/shiboken2/ApiExtractor/fileout.cpp index e3c96d57b..10a8f6be8 100644 --- a/sources/shiboken2/ApiExtractor/fileout.cpp +++ b/sources/shiboken2/ApiExtractor/fileout.cpp @@ -27,11 +27,13 @@ ****************************************************************************/ #include "fileout.h" +#include "messages.h" #include "reporthandler.h" #include <QtCore/QTextCodec> #include <QtCore/QFileInfo> #include <QtCore/QDir> +#include <QtCore/QDebug> #include <cstdio> @@ -50,24 +52,25 @@ static const char colorInfo[] = ""; static const char colorReset[] = ""; #endif -FileOut::FileOut(QString n): - name(n), - stream(&tmp), - isDone(false) -{} +FileOut::FileOut(QString n) : + name(std::move(n)), + stream(&tmp), + isDone(false) +{ +} + +FileOut::~FileOut() +{ + if (!isDone) + done(); +} -static int* lcsLength(QList<QByteArray> a, QList<QByteArray> b) +static QVector<int> lcsLength(const QByteArrayList &a, const QByteArrayList &b) { const int height = a.size() + 1; const int width = b.size() + 1; - int *res = new int[width * height]; - - for (int row = 0; row < height; row++) - res[width * row] = 0; - - for (int col = 0; col < width; col++) - res[col] = 0; + QVector<int> res(width * height, 0); for (int row = 1; row < height; row++) { for (int col = 1; col < width; col++) { @@ -89,88 +92,84 @@ enum Type { struct Unit { - Unit(Type type, int pos) : - type(type), - start(pos), - end(pos) {} - Type type; int start; int end; - void print(QList<QByteArray> a, QList<QByteArray> b) - { - if (type == Unchanged) { - if ((end - start) > 9) { - for (int i = start; i <= start + 2; i++) - std::printf(" %s\n", a[i].data()); - std::printf("%s=\n= %d more lines\n=%s\n", colorInfo, end - start - 6, colorReset); - for (int i = end - 2; i <= end; i++) - std::printf(" %s\n", a[i].data()); - } else { - for (int i = start; i <= end; i++) - std::printf(" %s\n", a[i].data()); - } - } else if (type == Add) { - std::printf("%s", colorAdd); - for (int i = start; i <= end; i++) - std::printf("+ %s\n", b[i].data()); - std::printf("%s", colorReset); - } else if (type == Delete) { - std::printf("%s", colorDelete); - for (int i = start; i <= end; i++) - std::printf("- %s\n", a[i].data()); - std::printf("%s", colorReset); - } - } + void print(const QByteArrayList &a, const QByteArrayList &b) const; }; -static QList<Unit*> *unitAppend(QList<Unit*> *res, Type type, int pos) +void Unit::print(const QByteArrayList &a, const QByteArrayList &b) const { - if (!res) { - res = new QList<Unit*>; - res->append(new Unit(type, pos)); - return res; + switch (type) { + case Unchanged: + if ((end - start) > 9) { + for (int i = start; i <= start + 2; i++) + std::printf(" %s\n", a.at(i).constData()); + std::printf("%s=\n= %d more lines\n=%s\n", + colorInfo, end - start - 6, colorReset); + for (int i = end - 2; i <= end; i++) + std::printf(" %s\n", a.at(i).constData()); + } else { + for (int i = start; i <= end; i++) + std::printf(" %s\n", a.at(i).constData()); + } + break; + case Add: + std::fputs(colorAdd, stdout); + for (int i = start; i <= end; i++) + std::printf("+ %s\n", b.at(i).constData()); + std::fputs(colorReset, stdout); + break; + case Delete: + std::fputs(colorDelete, stdout); + for (int i = start; i <= end; i++) + std::printf("- %s\n", a.at(i).constData()); + std::fputs(colorReset, stdout); + break; } +} - Unit *last = res->last(); - if (last->type == type) - last->end = pos; +static void unitAppend(Type type, int pos, QVector<Unit> *units) +{ + if (!units->isEmpty() && units->last().type == type) + units->last().end = pos; else - res->append(new Unit(type, pos)); - - return res; + units->append(Unit{type, pos, pos}); } -static QList<Unit*> *diffHelper(int *lcs, QList<QByteArray> a, QList<QByteArray> b, int row, int col) +static QVector<Unit> diffHelper(const QVector<int> &lcs, + const QByteArrayList &a, const QByteArrayList &b, + int row, int col) { - if (row > 0 && col > 0 && (a[row-1] == b[col-1])) { - return unitAppend(diffHelper(lcs, a, b, row - 1, col - 1), Unchanged, row - 1); - } else { - int width = b.size() + 1; - if ((col > 0) - && (row == 0 || lcs[width * row + col-1] >= lcs[width *(row-1) + col])) { - return unitAppend(diffHelper(lcs, a, b, row, col - 1), Add, col - 1); - } else if ((row > 0) - && (col == 0 || lcs[width * row + col-1] < lcs[width *(row-1) + col])) { - return unitAppend(diffHelper(lcs, a, b, row - 1, col), Delete, row - 1); - } + if (row > 0 && col > 0 && a.at(row - 1) == b.at(col - 1)) { + QVector<Unit> result = diffHelper(lcs, a, b, row - 1, col - 1); + unitAppend(Unchanged, row - 1, &result); + return result; } - delete lcs; - return 0; -} -static void diff(QList<QByteArray> a, QList<QByteArray> b) -{ - QList<Unit*> *res = diffHelper(lcsLength(a, b), a, b, a.size(), b.size()); - for (int i = 0; i < res->size(); i++) { - Unit *unit = res->at(i); - unit->print(a, b); - delete(unit); + const int width = b.size() + 1; + if (col > 0 + && (row == 0 || lcs.at(width * row + col -1 ) >= lcs.at(width * (row - 1) + col))) { + QVector<Unit> result = diffHelper(lcs, a, b, row, col - 1); + unitAppend(Add, col - 1, &result); + return result; } - delete(res); + if (row > 0 + && (col == 0 || lcs.at(width * row + col-1) < lcs.at(width * (row - 1) + col))) { + QVector<Unit> result = diffHelper(lcs, a, b, row - 1, col); + unitAppend(Delete, row - 1, &result); + return result; + } + return QVector<Unit>{}; } +static void diff(const QByteArrayList &a, const QByteArrayList &b) +{ + const QVector<Unit> res = diffHelper(lcsLength(a, b), a, b, a.size(), b.size()); + for (const Unit &unit : res) + unit.print(a, b); +} FileOut::State FileOut::done() { @@ -181,18 +180,6 @@ FileOut::State FileOut::done() return result; } -QString FileOut::msgCannotOpenForReading(const QFile &f) -{ - return QStringLiteral("Failed to open file '%1' for reading: %2") - .arg(QDir::toNativeSeparators(f.fileName()), f.errorString()); -} - -QString FileOut::msgCannotOpenForWriting(const QFile &f) -{ - return QStringLiteral("Failed to open file '%1' for writing: %2") - .arg(QDir::toNativeSeparators(f.fileName()), f.errorString()); -} - FileOut::State FileOut::done(QString *errorMessage) { Q_ASSERT(!isDone); @@ -245,3 +232,18 @@ FileOut::State FileOut::done(QString *errorMessage) return Success; } + +void FileOut::touchFile(const QString &filePath) +{ + QFile toucher(filePath); + qint64 size = toucher.size(); + if (!toucher.open(QIODevice::ReadWrite)) { + qCWarning(lcShiboken).noquote().nospace() + << QStringLiteral("Failed to touch file '%1'") + .arg(QDir::toNativeSeparators(filePath)); + return; + } + toucher.resize(size+1); + toucher.resize(size); + toucher.close(); +} diff --git a/sources/shiboken2/ApiExtractor/fileout.h b/sources/shiboken2/ApiExtractor/fileout.h index 539ae7a43..aace70131 100644 --- a/sources/shiboken2/ApiExtractor/fileout.h +++ b/sources/shiboken2/ApiExtractor/fileout.h @@ -43,18 +43,17 @@ private: public: enum State { Failure, Unchanged, Success }; - FileOut(QString name); - ~FileOut() - { - if (!isDone) - done(); - } + explicit FileOut(QString name); + ~FileOut(); + + QString filePath() const { return name; } State done(); State done(QString *errorMessage); - static QString msgCannotOpenForReading(const QFile &f); - static QString msgCannotOpenForWriting(const QFile &f); + void touch() { touchFile(name); } + + static void touchFile(const QString &filePath); QTextStream stream; diff --git a/sources/shiboken2/ApiExtractor/include.cpp b/sources/shiboken2/ApiExtractor/include.cpp index 963999b9d..d6a451992 100644 --- a/sources/shiboken2/ApiExtractor/include.cpp +++ b/sources/shiboken2/ApiExtractor/include.cpp @@ -36,10 +36,9 @@ QString Include::toString() const { if (m_type == IncludePath) return QLatin1String("#include <") + m_name + QLatin1Char('>'); - else if (m_type == LocalPath) + if (m_type == LocalPath) return QLatin1String("#include \"") + m_name + QLatin1Char('"'); - else - return QLatin1String("import ") + m_name + QLatin1Char(';'); + return QLatin1String("import ") + m_name + QLatin1Char(';'); } uint qHash(const Include& inc) diff --git a/sources/shiboken2/ApiExtractor/include.h b/sources/shiboken2/ApiExtractor/include.h index 16059876a..4890eea2c 100644 --- a/sources/shiboken2/ApiExtractor/include.h +++ b/sources/shiboken2/ApiExtractor/include.h @@ -42,7 +42,8 @@ public: enum IncludeType { IncludePath, LocalPath, - TargetLangImport + TargetLangImport, + InvalidInclude }; Include() : m_type(IncludePath) {} diff --git a/sources/shiboken2/ApiExtractor/messages.cpp b/sources/shiboken2/ApiExtractor/messages.cpp new file mode 100644 index 000000000..fa4c75743 --- /dev/null +++ b/sources/shiboken2/ApiExtractor/messages.cpp @@ -0,0 +1,446 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt for Python. +** +** $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$ +** +****************************************************************************/ + +#include "messages.h" +#include "abstractmetalang.h" +#include "typesystem.h" +#include <codemodel.h> + +#include <QtCore/QCoreApplication> +#include <QtCore/QDebug> +#include <QtCore/QDir> +#include <QtCore/QFile> +#include <QtCore/QStringList> +#include <QtCore/QXmlStreamReader> + +static inline QString colonColon() { return QStringLiteral("::"); } + +// abstractmetabuilder.cpp + +QString msgNoFunctionForModification(const QString &signature, + const QString &originalSignature, + const QString &className, + const QStringList &possibleSignatures, + const AbstractMetaFunctionList &allFunctions) +{ + QString result; + QTextStream str(&result); + str << "signature '" << signature << '\''; + if (!originalSignature.isEmpty() && originalSignature != signature) + str << " (specified as '" << originalSignature << "')"; + str << " for function modification in '" + << className << "' not found."; + if (!possibleSignatures.isEmpty()) { + str << "\n Possible candidates:\n"; + for (const auto &s : possibleSignatures) + str << " " << s << '\n'; + } else if (!allFunctions.isEmpty()) { + str << "\n No candidates were found. Member functions:\n"; + const int maxCount = qMin(10, allFunctions.size()); + for (int f = 0; f < maxCount; ++f) + str << " " << allFunctions.at(f)->minimalSignature() << '\n'; + if (maxCount < allFunctions.size()) + str << " ...\n"; + } + return result; +} + +template <class Stream> +static void msgFormatEnumType(Stream &str, + const EnumModelItem &enumItem, + const QString &className) +{ + switch (enumItem->enumKind()) { + case CEnum: + str << "Enum '" << enumItem->qualifiedName().join(colonColon()) << '\''; + break; + case AnonymousEnum: { + const EnumeratorList &values = enumItem->enumerators(); + str << "Anonymous enum ("; + switch (values.size()) { + case 0: + break; + case 1: + str << values.constFirst()->name(); + break; + case 2: + str << values.at(0)->name() << ", " << values.at(1)->name(); + break; + default: + str << values.at(0)->name() << ", ... , " + << values.at(values.size() - 1)->name(); + break; + } + str << ')'; + } + break; + case EnumClass: + str << "Scoped enum '" << enumItem->qualifiedName().join(colonColon()) << '\''; + break; + } + if (!className.isEmpty()) + str << " (class: " << className << ')'; +} + +QString msgNoEnumTypeEntry(const EnumModelItem &enumItem, + const QString &className) +{ + QString result; + QTextStream str(&result); + msgFormatEnumType(str, enumItem, className); + str << " does not have a type entry"; + return result; +} + +QString msgNoEnumTypeConflict(const EnumModelItem &enumItem, + const QString &className, + const TypeEntry *t) +{ + QString result; + QDebug debug(&result); // Use the debug operator for TypeEntry::Type + debug.noquote(); + debug.nospace(); + msgFormatEnumType(debug, enumItem, className); + debug << " is not an enum (type: " << t->type() << ')'; + return result; +} + +QString msgUnmatchedParameterType(const ArgumentModelItem &arg, int n, + const QString &why) +{ + QString result; + QTextStream str(&result); + str << "unmatched type '" << arg->type().toString() << "' in parameter #" + << (n + 1); + if (!arg->name().isEmpty()) + str << " \"" << arg->name() << '"'; + str << ": " << why; + return result; +} + +QString msgUnmatchedReturnType(const FunctionModelItem &functionItem, + const QString &why) +{ + return QLatin1String("unmatched return type '") + + functionItem->type().toString() + + QLatin1String("': ") + why; +} + +QString msgSkippingFunction(const FunctionModelItem &functionItem, + const QString &signature, const QString &why) +{ + QString result; + QTextStream str(&result); + str << "skipping "; + if (functionItem->isAbstract()) + str << "abstract "; + str << "function '" << signature << "', " << why; + if (functionItem->isAbstract()) { + str << "\nThis will lead to compilation errors due to not " + "being able to instantiate the wrapper."; + } + return result; +} + +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; +} + +QString msgUnableToTranslateType(const QString &t, const QString &why) +{ + return QLatin1String("Unable to translate type \"") + + t + QLatin1String("\": ") + why; +} + +QString msgUnableToTranslateType(const TypeInfo &typeInfo, + const QString &why) +{ + return msgUnableToTranslateType(typeInfo.toString(), why); +} + +QString msgCannotFindTypeEntry(const QString &t) +{ + return QLatin1String("Cannot find type entry for \"") + t + QLatin1String("\"."); +} + +QString msgCannotTranslateTemplateArgument(int i, + const TypeInfo &typeInfo, + const QString &why) +{ + QString result; + QTextStream(&result) << "Unable to translate template argument " + << (i + 1) << typeInfo.toString() << ": " << why; + return result; +} + +// abstractmetalang.cpp + +QString msgDisallowThread(const AbstractMetaFunction *f) +{ + QString result; + QTextStream str(&result); + str <<"Disallowing threads for "; + if (auto c = f->declaringClass()) + str << c->name() << "::"; + str << f->name() << "()."; + return result; +} + +// docparser.cpp + +QString msgCannotFindDocumentation(const QString &fileName, + const char *what, const QString &name, + const QString &query) +{ + QString result; + QTextStream(&result) << "Cannot find documentation for " << what + << ' ' << name << " in:\n " << QDir::toNativeSeparators(fileName) + << "\n using query:\n " << query; + return result; +} + +QString msgCannotFindDocumentation(const QString &fileName, + const AbstractMetaClass *metaClass, + const AbstractMetaFunction *function, + const QString &query) +{ + const QString name = metaClass->name() + QLatin1String("::") + + function->minimalSignature(); + return msgCannotFindDocumentation(fileName, "function", name, query); +} + +QString msgCannotFindDocumentation(const QString &fileName, + const AbstractMetaClass *metaClass, + const AbstractMetaEnum *e, + const QString &query) +{ + return msgCannotFindDocumentation(fileName, "enum", + metaClass->name() + QLatin1String("::") + e->name(), + query); +} + +QString msgCannotFindDocumentation(const QString &fileName, + const AbstractMetaClass *metaClass, + const AbstractMetaField *f, + const QString &query) +{ + return msgCannotFindDocumentation(fileName, "field", + metaClass->name() + QLatin1String("::") + f->name(), + query); +} + +QString msgXpathDocModificationError(const DocModificationList& mods, + const QString &what) +{ + QString result; + QTextStream str(&result); + str << "Error when applying modifications ("; + for (const DocModification &mod : mods) { + if (mod.mode() == TypeSystem::DocModificationXPathReplace) { + str << '"' << mod.xpath() << "\" -> \""; + const QString simplified = mod.code().simplified(); + if (simplified.size() > 20) + str << simplified.leftRef(20) << "..."; + else + str << simplified; + str << '"'; + } + } + str << "): " << what; + return result; +} + +// fileout.cpp + +QString msgCannotOpenForReading(const QFile &f) +{ + return QStringLiteral("Failed to open file '%1' for reading: %2") + .arg(QDir::toNativeSeparators(f.fileName()), f.errorString()); +} + +QString msgCannotOpenForWriting(const QFile &f) +{ + return QStringLiteral("Failed to open file '%1' for writing: %2") + .arg(QDir::toNativeSeparators(f.fileName()), f.errorString()); +} + +// generator.cpp + +QString msgCannotUseEnumAsInt(const QString &name) +{ + return QLatin1String("Cannot convert the protected scoped enum \"") + name + + QLatin1String("\" to type int when generating wrappers for the protected hack. " + "Compilation errors may occur when used as a function argument."); +} + +// main.cpp + +QString msgLeftOverArguments(const QMap<QString, QString> &remainingArgs) +{ + QString message; + QTextStream str(&message); + str << "shiboken: Called with wrong arguments:"; + for (auto it = remainingArgs.cbegin(), end = remainingArgs.cend(); it != end; ++it) { + str << ' ' << it.key(); + if (!it.value().isEmpty()) + str << ' ' << it.value(); + } + str << "\nCommand line: " << QCoreApplication::arguments().join(QLatin1Char(' ')); + return message; +} + +QString msgInvalidVersion(const QString &package, const QString &version) +{ + return QLatin1String("Invalid version \"") + version + + QLatin1String("\" specified for package ") + package + QLatin1Char('.'); +} + +QString msgCyclicDependency(const QString &funcName, const QString &graphName, + const QVector<const AbstractMetaFunction *> &involvedConversions) +{ + QString result; + QTextStream str(&result); + str << "Cyclic dependency found on overloaddata for \"" << funcName + << "\" method! The graph boy saved the graph at \"" + << QDir::toNativeSeparators(graphName) << "\"."; + if (const int count = involvedConversions.size()) { + str << " Implicit conversions (" << count << "): "; + for (int i = 0; i < count; ++i) { + if (i) + str << ", \""; + str << involvedConversions.at(i)->signature() << '"'; + if (const AbstractMetaClass *c = involvedConversions.at(i)->implementingClass()) + str << '(' << c->name() << ')'; + } + } + return result; +} + +// shibokengenerator.cpp + +QString msgUnknownOperator(const AbstractMetaFunction* func) +{ + QString result = QLatin1String("Unknown operator: \"") + func->originalName() + + QLatin1Char('"'); + if (const AbstractMetaClass *c = func->implementingClass()) + result += QLatin1String(" in class: ") + c->name(); + return result; +} + +QString msgWrongIndex(const char *varName, const QString &capture, + const AbstractMetaFunction *func) +{ + QString result; + QTextStream str(&result); + str << "Wrong index for " << varName << " variable (" << capture << ") on "; + if (const AbstractMetaClass *c = func->implementingClass()) + str << c->name() << "::"; + str << func->signature(); + return result; +} + +QString msgCannotFindType(const QString &type, const QString &variable, + const QString &why) +{ + QString result; + QTextStream(&result) << "Could not find type '" + << type << "' for use in '" << variable << "' conversion: " << why + << "\nMake sure to use the full C++ name, e.g. 'Namespace::Class'."; + return result; +} + +QString msgCannotBuildMetaType(const QString &s) +{ + return QLatin1String("Unable to build meta type for \"") + + s + QLatin1String("\": "); +} + +QString msgCouldNotFindMinimalConstructor(const QString &where, const QString &type) +{ + return where + QLatin1String(": Could not find a minimal constructor for type '") + + type + QLatin1String("'. This will result in a compilation error."); +} + +// typedatabase.cpp + +QString msgRejectReason(const TypeRejection &r, const QString &needle) +{ + QString result; + QTextStream str(&result); + switch (r.matchType) { + case TypeRejection::ExcludeClass: + str << " matches class exclusion \"" << r.className.pattern() << '"'; + break; + case TypeRejection::Function: + case TypeRejection::Field: + case TypeRejection::Enum: + str << " matches class \"" << r.className.pattern() << "\" and \"" + << r.pattern.pattern() << '"'; + break; + case TypeRejection::ArgumentType: + case TypeRejection::ReturnType: + str << " matches class \"" << r.className.pattern() << "\" and \"" + << needle << "\" matches \"" << r.pattern.pattern() << '"'; + break; + case TypeRejection::Invalid: + break; + } + return result; +} + +// qtdocgenerator.cpp + +QString msgTagWarning(const QXmlStreamReader &reader, const QString &context, + const QString &tag, const QString &message) +{ + QString result; + QTextStream str(&result); + str << "While handling <"; + const QStringRef currentTag = reader.name(); + if (currentTag.isEmpty()) + str << tag; + else + str << currentTag; + str << "> in " << context << ", line "<< reader.lineNumber() + << ": " << message; + return result; +} + +QString msgFallbackWarning(const QXmlStreamReader &reader, const QString &context, + const QString &tag, const QString &location, + const QString &identifier, const QString &fallback) +{ + QString message = QLatin1String("Falling back to \"") + + QDir::toNativeSeparators(fallback) + QLatin1String("\" for \"") + + location + QLatin1Char('"'); + if (!identifier.isEmpty()) + message += QLatin1String(" [") + identifier + QLatin1Char(']'); + return msgTagWarning(reader, context, tag, message); +} diff --git a/sources/shiboken2/ApiExtractor/messages.h b/sources/shiboken2/ApiExtractor/messages.h new file mode 100644 index 000000000..539332aef --- /dev/null +++ b/sources/shiboken2/ApiExtractor/messages.h @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt for Python. +** +** $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$ +** +****************************************************************************/ + +#ifndef MESSAGES_H +#define MESSAGES_H + +#include "abstractmetalang_typedefs.h" +#include "parser/codemodel_fwd.h" +#include "typesystem_typedefs.h" + +#include <QtCore/QMap> +#include <QtCore/QString> +#include <QtCore/QVector> + +class TypeEntry; +class TypeInfo; +struct TypeRejection; + +QT_FORWARD_DECLARE_CLASS(QDir) +QT_FORWARD_DECLARE_CLASS(QFile) +QT_FORWARD_DECLARE_CLASS(QXmlStreamReader) + +QString msgNoFunctionForModification(const QString &signature, + const QString &originalSignature, + const QString &className, + const QStringList &possibleSignatures, + const AbstractMetaFunctionList &allFunctions); + +QString msgNoEnumTypeEntry(const EnumModelItem &enumItem, + const QString &className); + + +QString msgNoEnumTypeConflict(const EnumModelItem &enumItem, + const QString &className, + const TypeEntry *t); + +QString msgUnmatchedParameterType(const ArgumentModelItem &arg, int n, + const QString &why); + +QString msgUnmatchedReturnType(const FunctionModelItem &functionItem, + const QString &why); + +QString msgSkippingFunction(const FunctionModelItem &functionItem, + const QString &signature, const QString &why); + +QString msgCannotSetArrayUsage(const QString &function, int i, const QString &reason); + +QString msgUnableToTranslateType(const QString &t, const QString &why); + +QString msgUnableToTranslateType(const TypeInfo &typeInfo, + const QString &why); + +QString msgCannotFindTypeEntry(const QString &t); + +QString msgCannotTranslateTemplateArgument(int i, + const TypeInfo &typeInfo, + const QString &why); + +QString msgDisallowThread(const AbstractMetaFunction *f); + +QString msgCannotFindDocumentation(const QString &fileName, + const char *what, const QString &name, + const QString &query); + +QString msgCannotFindDocumentation(const QString &fileName, + const AbstractMetaClass *metaClass, + const AbstractMetaFunction *function, + const QString &query); + +QString msgCannotFindDocumentation(const QString &fileName, + const AbstractMetaClass *metaClass, + const AbstractMetaEnum *e, + const QString &query); + +QString msgCannotFindDocumentation(const QString &fileName, + const AbstractMetaClass *metaClass, + const AbstractMetaField *f, + const QString &query); + +QString msgXpathDocModificationError(const DocModificationList& mods, + const QString &what); + +QString msgCannotOpenForReading(const QFile &f); + +QString msgCannotOpenForWriting(const QFile &f); + +QString msgCannotUseEnumAsInt(const QString &name); + +QString msgLeftOverArguments(const QMap<QString, QString> &remainingArgs); + +QString msgInvalidVersion(const QString &package, const QString &version); + +QString msgCyclicDependency(const QString &funcName, const QString &graphName, + const QVector<const AbstractMetaFunction *> &involvedConversions); + +QString msgUnknownOperator(const AbstractMetaFunction* func); + +QString msgWrongIndex(const char *varName, const QString &capture, + const AbstractMetaFunction *func); + +QString msgCannotFindType(const QString &type, const QString &variable, + const QString &why); + +QString msgCannotBuildMetaType(const QString &s); + +QString msgCouldNotFindMinimalConstructor(const QString &where, const QString &type); + +QString msgRejectReason(const TypeRejection &r, const QString &needle = QString()); + +QString msgTagWarning(const QXmlStreamReader &reader, const QString &context, + const QString &tag, const QString &message); + +QString msgFallbackWarning(const QXmlStreamReader &reader, const QString &context, + const QString &tag, const QString &location, + const QString &identifier, const QString &fallback); + +#endif // MESSAGES_H diff --git a/sources/shiboken2/ApiExtractor/parser/codemodel.cpp b/sources/shiboken2/ApiExtractor/parser/codemodel.cpp index d862692dd..173f6dd23 100644 --- a/sources/shiboken2/ApiExtractor/parser/codemodel.cpp +++ b/sources/shiboken2/ApiExtractor/parser/codemodel.cpp @@ -29,11 +29,15 @@ #include "codemodel.h" + +#include <clangparser/clangutils.h> + #include <algorithm> #include <functional> #include <iostream> #include <QDebug> #include <QDir> +#include <QtCore/QStack> // Predicate to find an item by name in a list of QSharedPointer<Item> template <class T> class ModelItemNamePredicate : public std::unary_function<bool, QSharedPointer<T> > @@ -140,16 +144,18 @@ TypeInfo TypeInfo::combine(const TypeInfo &__lhs, const TypeInfo &__rhs) __result.setVolatile(__result.isVolatile() || __rhs.isVolatile()); if (__rhs.referenceType() > __result.referenceType()) __result.setReferenceType(__rhs.referenceType()); - __result.setIndirections(__result.indirections() + __rhs.indirections()); + __result.m_indirections.append(__rhs.m_indirections); __result.setArrayElements(__result.arrayElements() + __rhs.arrayElements()); + __result.m_instantiations.append(__rhs.m_instantiations); return __result; } bool TypeInfo::isVoid() const { - return m_indirections == 0 && m_referenceType == NoReference + return m_indirections.isEmpty() && m_referenceType == NoReference && m_arguments.isEmpty() && m_arrayElements.isEmpty() + && m_instantiations.isEmpty() && m_qualifiedName.size() == 1 && m_qualifiedName.constFirst() == QLatin1String("void"); } @@ -193,19 +199,76 @@ TypeInfo TypeInfo::resolveType(CodeModelItem __item, TypeInfo const &__type, Cod return otherType; } +// Handler for clang::parseTemplateArgumentList() that populates +// TypeInfo::m_instantiations +class TypeInfoTemplateArgumentHandler : + public std::binary_function<void, int, const QStringRef &> +{ +public: + explicit TypeInfoTemplateArgumentHandler(TypeInfo *t) + { + m_parseStack.append(t); + } + + void operator()(int level, const QStringRef &name) + { + if (level > m_parseStack.size()) { + Q_ASSERT(!top()->m_instantiations.isEmpty()); + m_parseStack.push(&top()->m_instantiations.back()); + } + while (level < m_parseStack.size()) + m_parseStack.pop(); + TypeInfo instantiation; + instantiation.setQualifiedName(qualifiedName(name)); + top()->addInstantiation(instantiation); + } + +private: + TypeInfo *top() const { return m_parseStack.back(); } + + static QStringList qualifiedName(const QStringRef &name) + { + QStringList result; + const QVector<QStringRef> nameParts = name.split(QLatin1String("::")); + result.reserve(nameParts.size()); + for (const QStringRef &p : nameParts) + result.append(p.toString()); + return result; + } + + QStack<TypeInfo *> m_parseStack; +}; + +QPair<int, int> TypeInfo::parseTemplateArgumentList(const QString &l, int from) +{ + return clang::parseTemplateArgumentList(l, clang::TemplateArgumentHandler(TypeInfoTemplateArgumentHandler(this)), from); +} + QString TypeInfo::toString() const { QString tmp; - - tmp += m_qualifiedName.join(QLatin1String("::")); if (isConstant()) - tmp += QLatin1String(" const"); + tmp += QLatin1String("const "); if (isVolatile()) - tmp += QLatin1String(" volatile"); + tmp += QLatin1String("volatile "); - if (indirections()) - tmp += QString(indirections(), QLatin1Char('*')); + tmp += m_qualifiedName.join(QLatin1String("::")); + + if (const int instantiationCount = m_instantiations.size()) { + tmp += QLatin1Char('<'); + for (int i = 0; i < instantiationCount; ++i) { + if (i) + tmp += QLatin1String(", "); + tmp += m_instantiations.at(i).toString(); + } + if (tmp.endsWith(QLatin1Char('>'))) + tmp += QLatin1Char(' '); + tmp += QLatin1Char('>'); + } + + for (Indirection i : m_indirections) + tmp.append(indirectionKeyword(i)); switch (referenceType()) { case NoReference: @@ -238,20 +301,6 @@ QString TypeInfo::toString() const return tmp; } -QStringList TypeInfo::instantiationName() const -{ - QStringList result = m_qualifiedName; - if (const int argumentCount = m_arguments.size()) { - QString &last = result.last(); - for (int i = 0; i < argumentCount; ++i) { - last += i ? QLatin1String(", ") : QLatin1String("< "); - last += m_arguments.at(i).toString(); - } - last += QLatin1String(" >"); - } - return result; -} - bool TypeInfo::operator==(const TypeInfo &other) const { if (arrayElements().count() != other.arrayElements().count()) @@ -269,7 +318,73 @@ bool TypeInfo::operator==(const TypeInfo &other) const return flags == other.flags && m_qualifiedName == other.m_qualifiedName - && (!m_functionPointer || m_arguments == other.m_arguments); + && (!m_functionPointer || m_arguments == other.m_arguments) + && m_instantiations == other.m_instantiations; +} + +QString TypeInfo::indirectionKeyword(Indirection i) +{ + return i == Indirection::Pointer + ? QStringLiteral("*") : QStringLiteral("*const"); +} + +static inline QString constQualifier() { return QStringLiteral("const"); } +static inline QString volatileQualifier() { return QStringLiteral("volatile"); } + +bool TypeInfo::stripLeadingConst(QString *s) +{ + return stripLeadingQualifier(constQualifier(), s); +} + +bool TypeInfo::stripLeadingVolatile(QString *s) +{ + return stripLeadingQualifier(volatileQualifier(), s); +} + +bool TypeInfo::stripLeadingQualifier(const QString &qualifier, QString *s) +{ + // "const int x" + const int qualifierSize = qualifier.size(); + if (s->size() < qualifierSize + 1 || !s->startsWith(qualifier) + || !s->at(qualifierSize).isSpace()) { + return false; + } + s->remove(0, qualifierSize + 1); + while (!s->isEmpty() && s->at(0).isSpace()) + s->remove(0, 1); + return true; +} + +// Helper functionality to simplify a raw standard type as returned by +// clang_getCanonicalType() for g++ standard containers from +// "std::__cxx11::list<int, std::allocator<int> >" or +// "std::__1::list<int, std::allocator<int> >" -> "std::list<int>". + +bool TypeInfo::isStdType() const +{ + return m_qualifiedName.size() > 1 + && m_qualifiedName.constFirst() == QLatin1String("std"); +} + +static inline bool discardStdType(const QString &name) +{ + return name == QLatin1String("allocator") || name == QLatin1String("less"); +} + +void TypeInfo::simplifyStdType() +{ + if (isStdType()) { + if (m_qualifiedName.at(1).startsWith(QLatin1String("__"))) + m_qualifiedName.removeAt(1); + for (int t = m_instantiations.size() - 1; t >= 0; --t) { + if (m_instantiations.at(t).isStdType()) { + if (discardStdType(m_instantiations.at(t).m_qualifiedName.constLast())) + m_instantiations.removeAt(t); + else + m_instantiations[t].simplifyStdType(); + } + } + } } #ifndef QT_NO_DEBUG_STREAM @@ -292,8 +407,11 @@ void TypeInfo::formatDebug(QDebug &d) const d << ", [const]"; if (m_volatile) d << ", [volatile]"; - if (m_indirections) - d << ", indirections=" << m_indirections; + if (!m_indirections.isEmpty()) { + d << ", indirections="; + for (auto i : m_indirections) + d << ' ' << TypeInfo::indirectionKeyword(i); + } switch (m_referenceType) { case NoReference: break; @@ -304,6 +422,11 @@ void TypeInfo::formatDebug(QDebug &d) const d << ", [rvalref]"; break; } + if (!m_instantiations.isEmpty()) { + d << ", template<"; + formatSequence(d, m_instantiations.begin(), m_instantiations.end()); + d << '>'; + } if (m_functionPointer) { d << ", function ptr("; formatSequence(d, m_arguments.begin(), m_arguments.end()); @@ -358,9 +481,7 @@ _CodeModelItem::_CodeModelItem(CodeModel *model, const QString &name, int kind) { } -_CodeModelItem::~_CodeModelItem() -{ -} +_CodeModelItem::~_CodeModelItem() = default; int _CodeModelItem::kind() const { @@ -532,9 +653,7 @@ QDebug operator<<(QDebug d, const _CodeModelItem *t) #endif // !QT_NO_DEBUG_STREAM // --------------------------------------------------------------------------- -_ClassModelItem::~_ClassModelItem() -{ -} +_ClassModelItem::~_ClassModelItem() = default; TemplateParameterList _ClassModelItem::templateParameters() const { @@ -624,9 +743,7 @@ FunctionModelItem _ScopeModelItem::declaredFunction(FunctionModelItem item) return FunctionModelItem(); } -_ScopeModelItem::~_ScopeModelItem() -{ -} +_ScopeModelItem::~_ScopeModelItem() = default; void _ScopeModelItem::addEnumsDeclaration(const QString &enumsDeclaration) { @@ -835,11 +952,9 @@ void _ArgumentModelItem::formatDebug(QDebug &d) const } #endif // !QT_NO_DEBUG_STREAM // --------------------------------------------------------------------------- -_FunctionModelItem::~_FunctionModelItem() -{ -} +_FunctionModelItem::~_FunctionModelItem() = default; -bool _FunctionModelItem::isSimilar(FunctionModelItem other) const +bool _FunctionModelItem::isSimilar(const FunctionModelItem &other) const { if (name() != other->name()) return false; @@ -871,7 +986,7 @@ ArgumentList _FunctionModelItem::arguments() const return m_arguments; } -void _FunctionModelItem::addArgument(ArgumentModelItem item) +void _FunctionModelItem::addArgument(const ArgumentModelItem& item) { m_arguments.append(item); } @@ -896,6 +1011,21 @@ void _FunctionModelItem::setVariadics(bool isVariadics) m_isVariadics = isVariadics; } +bool _FunctionModelItem::isNoExcept() const +{ + return m_exceptionSpecification == ExceptionSpecification::NoExcept; +} + +ExceptionSpecification _FunctionModelItem::exceptionSpecification() const +{ + return m_exceptionSpecification; +} + +void _FunctionModelItem::setExceptionSpecification(ExceptionSpecification e) +{ + m_exceptionSpecification = e; +} + bool _FunctionModelItem::isDeleted() const { return m_isDeleted; @@ -991,7 +1121,7 @@ void _FunctionModelItem::setInvokable(bool isInvokable) void _FunctionModelItem::formatDebug(QDebug &d) const { _MemberModelItem::formatDebug(d); - d << ", type=" << m_functionType; + d << ", type=" << m_functionType << ", exspec=" << int(m_exceptionSpecification); if (m_isDeleted) d << " [deleted!]"; if (m_isInline) @@ -1091,9 +1221,7 @@ void _EnumModelItem::formatDebug(QDebug &d) const #endif // !QT_NO_DEBUG_STREAM // --------------------------------------------------------------------------- -_EnumeratorModelItem::~_EnumeratorModelItem() -{ -} +_EnumeratorModelItem::~_EnumeratorModelItem() = default; QString _EnumeratorModelItem::stringValue() const { @@ -1114,9 +1242,7 @@ void _EnumeratorModelItem::formatDebug(QDebug &d) const #endif // !QT_NO_DEBUG_STREAM // --------------------------------------------------------------------------- -_TemplateParameterModelItem::~_TemplateParameterModelItem() -{ -} +_TemplateParameterModelItem::~_TemplateParameterModelItem() = default; TypeInfo _TemplateParameterModelItem::type() const { @@ -1164,9 +1290,7 @@ CodeModel::AccessPolicy _MemberModelItem::accessPolicy() const return m_accessPolicy; } -_MemberModelItem::~_MemberModelItem() -{ -} +_MemberModelItem::~_MemberModelItem() = default; void _MemberModelItem::setAccessPolicy(CodeModel::AccessPolicy accessPolicy) { diff --git a/sources/shiboken2/ApiExtractor/parser/codemodel.h b/sources/shiboken2/ApiExtractor/parser/codemodel.h index ac1fe26c1..7bd82bd1d 100644 --- a/sources/shiboken2/ApiExtractor/parser/codemodel.h +++ b/sources/shiboken2/ApiExtractor/parser/codemodel.h @@ -36,6 +36,7 @@ #include "enumvalue.h" #include <QtCore/QHash> +#include <QtCore/QPair> #include <QtCore/QSet> #include <QtCore/QString> #include <QtCore/QStringList> @@ -100,6 +101,8 @@ class TypeInfo { friend class TypeParser; public: + typedef QVector<Indirection> Indirections; + TypeInfo() : flags(0), m_referenceType(NoReference) {} QStringList qualifiedName() const @@ -137,14 +140,16 @@ public: ReferenceType referenceType() const { return m_referenceType; } void setReferenceType(ReferenceType r) { m_referenceType = r; } - int indirections() const - { - return m_indirections; - } + Indirections indirectionsV() const { return m_indirections; } + void setIndirectionsV(const Indirections &i) { m_indirections = i; } + void addIndirection(Indirection i) { m_indirections.append(i); } + + // "Legacy", rename? + int indirections() const { return m_indirections.size(); } void setIndirections(int indirections) { - m_indirections = indirections; + m_indirections = Indirections(indirections, Indirection::Pointer); } bool isFunctionPointer() const @@ -165,6 +170,8 @@ public: m_arrayElements = arrayElements; } + void addArrayElement(const QString &a) { m_arrayElements.append(a); } + QVector<TypeInfo> arguments() const { return m_arguments; } void setArguments(const QVector<TypeInfo> &arguments); @@ -174,6 +181,15 @@ public: m_arguments.append(arg); } + QVector<TypeInfo> instantiations() const { return m_instantiations; } + void setInstantiations(const QVector<TypeInfo> &i) { m_instantiations = i; } + void addInstantiation(const TypeInfo &i) { m_instantiations.append(i); } + void clearInstantiations() { m_instantiations.clear(); } + + bool isStdType() const; + + QPair<int, int> parseTemplateArgumentList(const QString &l, int from = 0); + bool operator==(const TypeInfo &other) const; bool operator!=(const TypeInfo &other) const @@ -185,8 +201,6 @@ public: QString toString() const; - QStringList instantiationName() const; - static TypeInfo combine(const TypeInfo &__lhs, const TypeInfo &__rhs); static TypeInfo resolveType(TypeInfo const &__type, CodeModelItem __scope); @@ -194,12 +208,24 @@ public: void formatDebug(QDebug &d) const; #endif + static QString indirectionKeyword(Indirection i); + + static bool stripLeadingConst(QString *s); + static bool stripLeadingVolatile(QString *s); + static bool stripLeadingQualifier(const QString &qualifier, QString *s); + + void simplifyStdType(); + private: + friend class TypeInfoTemplateArgumentHandler; + static TypeInfo resolveType(CodeModelItem item, TypeInfo const &__type, CodeModelItem __scope); QStringList m_qualifiedName; QStringList m_arrayElements; QVector<TypeInfo> m_arguments; + QVector<TypeInfo> m_instantiations; + Indirections m_indirections; union { uint flags; @@ -208,8 +234,7 @@ private: uint m_constant: 1; uint m_volatile: 1; uint m_functionPointer: 1; - uint m_indirections: 6; - uint m_padding: 23; + uint m_padding: 29; }; }; @@ -547,7 +572,7 @@ public: ArgumentList arguments() const; - void addArgument(ArgumentModelItem item); + void addArgument(const ArgumentModelItem& item); CodeModel::FunctionType functionType() const; void setFunctionType(CodeModel::FunctionType functionType); @@ -582,7 +607,13 @@ public: bool isVariadics() const; void setVariadics(bool isVariadics); - bool isSimilar(FunctionModelItem other) const; + + bool isSimilar(const FunctionModelItem &other) const; + + bool isNoExcept() const; + + ExceptionSpecification exceptionSpecification() const; + void setExceptionSpecification(ExceptionSpecification e); #ifndef QT_NO_DEBUG_STREAM void formatDebug(QDebug &d) const override; @@ -606,6 +637,7 @@ private: }; uint m_flags; }; + ExceptionSpecification m_exceptionSpecification = ExceptionSpecification::Unknown; }; class _VariableModelItem: public _MemberModelItem diff --git a/sources/shiboken2/ApiExtractor/parser/codemodel_enums.h b/sources/shiboken2/ApiExtractor/parser/codemodel_enums.h index b8a10ba93..1713ba42f 100644 --- a/sources/shiboken2/ApiExtractor/parser/codemodel_enums.h +++ b/sources/shiboken2/ApiExtractor/parser/codemodel_enums.h @@ -41,4 +41,17 @@ enum EnumKind { EnumClass // C++ 11 : enum class Foo { value1, value2 } }; +enum class Indirection +{ + Pointer, // int * + ConstPointer // int *const +}; + +enum class ExceptionSpecification +{ + Unknown, + NoExcept, + Throws +}; + #endif // CODEMODEL_ENUMS_H diff --git a/sources/shiboken2/ApiExtractor/parser/enumvalue.h b/sources/shiboken2/ApiExtractor/parser/enumvalue.h index 4905e89ba..ea30c39bb 100644 --- a/sources/shiboken2/ApiExtractor/parser/enumvalue.h +++ b/sources/shiboken2/ApiExtractor/parser/enumvalue.h @@ -49,6 +49,7 @@ public: Type type() { return m_type; } qint64 value() const { return m_value; } quint64 unsignedValue() const { return m_unsignedValue; } + bool isNullValue() const { return m_type == Signed ? m_value == 0 : m_unsignedValue == 0u; } void setValue(qint64 v); void setUnsignedValue(quint64 v); diff --git a/sources/shiboken2/ApiExtractor/qtdocparser.cpp b/sources/shiboken2/ApiExtractor/qtdocparser.cpp index b0058d6ea..809760450 100644 --- a/sources/shiboken2/ApiExtractor/qtdocparser.cpp +++ b/sources/shiboken2/ApiExtractor/qtdocparser.cpp @@ -28,6 +28,7 @@ #include "qtdocparser.h" #include "abstractmetalang.h" +#include "messages.h" #include "reporthandler.h" #include "typesystem.h" diff --git a/sources/shiboken2/ApiExtractor/tests/CMakeLists.txt b/sources/shiboken2/ApiExtractor/tests/CMakeLists.txt index 860a37d9d..e100ef493 100644 --- a/sources/shiboken2/ApiExtractor/tests/CMakeLists.txt +++ b/sources/shiboken2/ApiExtractor/tests/CMakeLists.txt @@ -4,13 +4,18 @@ find_package(Qt5Test) find_package(Qt5Xml) find_package(Qt5XmlPatterns) +set(CMAKE_AUTORCC ON) + macro(declare_test testname) # gone: qt4_automoc("${testname}.cpp") - if (EXISTS "${testname}.h") - add_executable(${testname} "${testname}.h ${testname}.cpp") - else () - add_executable(${testname} "${testname}.cpp") + set(SOURCES "${testname}.cpp") + if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${testname}.h") + list(APPEND SOURCES "${testname}.h") + endif () + if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${testname}.qrc") + list(APPEND SOURCES "${testname}.qrc") endif () + add_executable(${testname} ${SOURCES}) include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${apiextractor_SOURCE_DIR} @@ -35,8 +40,6 @@ declare_test(testabstractmetatype) declare_test(testaddfunction) declare_test(testarrayargument) declare_test(testcodeinjection) -configure_file("${CMAKE_CURRENT_SOURCE_DIR}/utf8code.txt" - "${CMAKE_CURRENT_BINARY_DIR}/utf8code.txt" COPYONLY) declare_test(testcontainer) declare_test(testconversionoperator) declare_test(testconversionruletag) @@ -68,7 +71,5 @@ declare_test(testvoidarg) declare_test(testtyperevision) if (NOT DISABLE_DOCSTRINGS) declare_test(testmodifydocumentation) - configure_file("${CMAKE_CURRENT_SOURCE_DIR}/a.xml" - "${CMAKE_CURRENT_BINARY_DIR}/a.xml" COPYONLY) endif() diff --git a/sources/shiboken2/ApiExtractor/tests/injectedcode.txt b/sources/shiboken2/ApiExtractor/tests/injectedcode.txt new file mode 100644 index 000000000..872898810 --- /dev/null +++ b/sources/shiboken2/ApiExtractor/tests/injectedcode.txt @@ -0,0 +1,5 @@ +// Bla +// @snippet label +code line +// @snippet label +// Bla diff --git a/sources/shiboken2/ApiExtractor/tests/testabstractmetatype.cpp b/sources/shiboken2/ApiExtractor/tests/testabstractmetatype.cpp index 7f1361a7d..fc67ebba5 100644 --- a/sources/shiboken2/ApiExtractor/tests/testabstractmetatype.cpp +++ b/sources/shiboken2/ApiExtractor/tests/testabstractmetatype.cpp @@ -31,6 +31,37 @@ #include "testutil.h" #include <abstractmetalang.h> #include <typesystem.h> +#include <parser/codemodel.h> +#include <typeparser.h> + +void TestAbstractMetaType::parsing_data() +{ + QTest::addColumn<QString>("input"); + QTest::addColumn<QString>("output"); + QTest::newRow("primitive") + << QString::fromLatin1("int") << QString::fromLatin1("int"); + QTest::newRow("ref") + << QString::fromLatin1("int &") << QString::fromLatin1("int&"); + QTest::newRow("pointer") + << QString::fromLatin1("int **") << QString::fromLatin1("int**"); + QTest::newRow("const ref") + << QString::fromLatin1("const int &") << QString::fromLatin1("const int&"); + QTest::newRow("const pointer") + << QString::fromLatin1("const int **") << QString::fromLatin1("const int**"); + QTest::newRow("const pointer const") + << QString::fromLatin1("const int *const*") << QString::fromLatin1("const int*const*"); +} + +void TestAbstractMetaType::parsing() +{ + QFETCH(QString, input); + QFETCH(QString, output); + QString errorMessage; + const TypeInfo ti = TypeParser::parse(input, &errorMessage); + QVERIFY2(errorMessage.isEmpty(), qPrintable(errorMessage)); + const QString actual = ti.toString(); + QCOMPARE(actual, output); +} void TestAbstractMetaType::testConstCharPtrType() { @@ -72,7 +103,8 @@ void TestAbstractMetaType::testApiVersionSupported() <function signature='justAtest2()' since='1.1'/>\n\ <function signature='justAtest3()'/>\n\ </typesystem>\n"; - QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false, "1.0")); + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, + false, QLatin1String("1.0"))); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); @@ -90,7 +122,8 @@ void TestAbstractMetaType::testApiVersionNotSupported() const char* xmlCode = "<typesystem package='Foo'>\n\ <value-type name='object' since='0.1'/>\n\ </typesystem>\n"; - QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, true, "0.1")); + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, + true, QLatin1String("0.1"))); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); diff --git a/sources/shiboken2/ApiExtractor/tests/testabstractmetatype.h b/sources/shiboken2/ApiExtractor/tests/testabstractmetatype.h index b2aa7544f..b39a27a54 100644 --- a/sources/shiboken2/ApiExtractor/tests/testabstractmetatype.h +++ b/sources/shiboken2/ApiExtractor/tests/testabstractmetatype.h @@ -35,6 +35,8 @@ class TestAbstractMetaType : public QObject { Q_OBJECT private slots: + void parsing_data(); + void parsing(); void testConstCharPtrType(); void testCharType(); void testTypedef(); diff --git a/sources/shiboken2/ApiExtractor/tests/testaddfunction.cpp b/sources/shiboken2/ApiExtractor/tests/testaddfunction.cpp index 2a953243e..db49942c9 100644 --- a/sources/shiboken2/ApiExtractor/tests/testaddfunction.cpp +++ b/sources/shiboken2/ApiExtractor/tests/testaddfunction.cpp @@ -366,7 +366,8 @@ void TestAddFunction::testAddFunctionWithApiVersion() <inject-code class='target' position='beginning'>custom_code();</inject-code>\n\ </add-function>\n\ </typesystem>\n"; - QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, true, "0.1")); + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, + true, QLatin1String("0.1"))); QVERIFY(!builder.isNull()); AbstractMetaFunctionList globalFuncs = builder->globalFunctions(); QCOMPARE(globalFuncs.count(), 1); diff --git a/sources/shiboken2/ApiExtractor/tests/testcodeinjection.cpp b/sources/shiboken2/ApiExtractor/tests/testcodeinjection.cpp index 7bbde3bd4..9f71b495a 100644 --- a/sources/shiboken2/ApiExtractor/tests/testcodeinjection.cpp +++ b/sources/shiboken2/ApiExtractor/tests/testcodeinjection.cpp @@ -34,19 +34,43 @@ #include <abstractmetalang.h> #include <typesystem.h> -void TestCodeInjections::testReadFileUtf8() +void TestCodeInjections::testReadFile_data() { + QTest::addColumn<QString>("filePath"); + QTest::addColumn<QString>("snippet"); + QTest::addColumn<QString>("expected"); + + QTest::newRow("utf8") + << QString::fromLatin1(":/utf8code.txt") + << QString() + << QString::fromUtf8("\xC3\xA1\xC3\xA9\xC3\xAD\xC3\xB3\xC3\xBA"); + + QTest::newRow("snippet") + << QString::fromLatin1(":/injectedcode.txt") + << QString::fromLatin1("label") + << QString::fromLatin1("code line"); +} + +void TestCodeInjections::testReadFile() +{ + QFETCH(QString, filePath); + QFETCH(QString, snippet); + QFETCH(QString, expected); + const char* cppCode ="struct A {};\n"; int argc = 0; char *argv[] = {NULL}; QCoreApplication app(argc, argv); - QString filePath = QDir::currentPath(); + + QString attribute = QLatin1String("file='") + filePath + QLatin1Char('\''); + if (!snippet.isEmpty()) + attribute += QLatin1String(" snippet='") + snippet + QLatin1Char('\''); + QString xmlCode = QLatin1String("\ <typesystem package=\"Foo\">\n\ <value-type name='A'>\n\ - <conversion-rule file='") + filePath + QLatin1String("/utf8code.txt'/>\n\ - <inject-code class='target' file='") + filePath - + QLatin1String("/utf8code.txt'/>\n\ + <conversion-rule ") + attribute + QLatin1String("/>\n\ + <inject-code class='target' ") + attribute + QLatin1String("/>\n\ </value-type>\n\ <value-type name='A::B'/>\n\ </typesystem>\n"); @@ -56,10 +80,9 @@ void TestCodeInjections::testReadFileUtf8() const AbstractMetaClass *classA = AbstractMetaClass::findClass(classes, QLatin1String("A")); QCOMPARE(classA->typeEntry()->codeSnips().count(), 1); QString code = classA->typeEntry()->codeSnips().first().code(); - QString utf8Data = QString::fromUtf8("\xC3\xA1\xC3\xA9\xC3\xAD\xC3\xB3\xC3\xBA"); - QVERIFY(code.indexOf(utf8Data) != -1); + QVERIFY(code.indexOf(expected) != -1); code = classA->typeEntry()->conversionRule(); - QVERIFY(code.indexOf(utf8Data) != -1); + QVERIFY(code.indexOf(expected) != -1); } void TestCodeInjections::testInjectWithValidApiVersion() @@ -74,7 +97,8 @@ void TestCodeInjections::testInjectWithValidApiVersion() </value-type>\n\ </typesystem>\n"; - QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, true, "1.0")); + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, + true, QLatin1String("1.0"))); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); AbstractMetaClass* classA = AbstractMetaClass::findClass(classes, QLatin1String("A")); @@ -93,7 +117,8 @@ void TestCodeInjections::testInjectWithInvalidApiVersion() </value-type>\n\ </typesystem>\n"; - QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, true, "0.1")); + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, + true, QLatin1String("0.1"))); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); diff --git a/sources/shiboken2/ApiExtractor/tests/testcodeinjection.h b/sources/shiboken2/ApiExtractor/tests/testcodeinjection.h index bd5e7ece1..1ac873970 100644 --- a/sources/shiboken2/ApiExtractor/tests/testcodeinjection.h +++ b/sources/shiboken2/ApiExtractor/tests/testcodeinjection.h @@ -37,7 +37,8 @@ class TestCodeInjections : public QObject { Q_OBJECT private slots: - void testReadFileUtf8(); + void testReadFile_data(); + void testReadFile(); void testInjectWithValidApiVersion(); void testInjectWithInvalidApiVersion(); }; diff --git a/sources/shiboken2/ApiExtractor/tests/testcodeinjection.qrc b/sources/shiboken2/ApiExtractor/tests/testcodeinjection.qrc new file mode 100644 index 000000000..fd7616bd2 --- /dev/null +++ b/sources/shiboken2/ApiExtractor/tests/testcodeinjection.qrc @@ -0,0 +1,6 @@ +<RCC> + <qresource> + <file>utf8code.txt</file> + <file>injectedcode.txt</file> + </qresource> +</RCC> diff --git a/sources/shiboken2/ApiExtractor/tests/testdroptypeentries.cpp b/sources/shiboken2/ApiExtractor/tests/testdroptypeentries.cpp index b46c23f56..6abebb922 100644 --- a/sources/shiboken2/ApiExtractor/tests/testdroptypeentries.cpp +++ b/sources/shiboken2/ApiExtractor/tests/testdroptypeentries.cpp @@ -70,7 +70,8 @@ void TestDropTypeEntries::testDropEntries() droppedEntries << QLatin1String("Foo.ObjectB") << QLatin1String("Foo.NamespaceA.InnerClassA"); droppedEntries << QLatin1String("Foo.NamespaceB") << QLatin1String("Foo.EnumB") << QLatin1String("Foo.funcB()"); droppedEntries << QLatin1String("Foo.NamespaceA.InnerNamespaceA"); - QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false, Q_NULLPTR, droppedEntries)); + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false, + QString(), droppedEntries)); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); @@ -129,7 +130,8 @@ static const char* xmlCode2 = "\ void TestDropTypeEntries::testDropEntryWithChildTags() { QStringList droppedEntries(QLatin1String("Foo.ValueA")); - QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode2, xmlCode2, false, Q_NULLPTR, droppedEntries)); + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode2, xmlCode2, false, + QString(), droppedEntries)); QVERIFY(!builder.isNull()); QVERIFY(!AbstractMetaClass::findClass(builder->classes(), QLatin1String("ValueA"))); } diff --git a/sources/shiboken2/ApiExtractor/tests/testenum.cpp b/sources/shiboken2/ApiExtractor/tests/testenum.cpp index 87f2608a1..ebdcf8d81 100644 --- a/sources/shiboken2/ApiExtractor/tests/testenum.cpp +++ b/sources/shiboken2/ApiExtractor/tests/testenum.cpp @@ -104,7 +104,8 @@ void TestEnum::testEnumWithApiVersion() </value-type>\n\ </typesystem>\n"; - QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, true, "0.1")); + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, + true, QLatin1String("0.1"))); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); QCOMPARE(classes.count(), 1); diff --git a/sources/shiboken2/ApiExtractor/tests/testmodifydocumentation.cpp b/sources/shiboken2/ApiExtractor/tests/testmodifydocumentation.cpp index 7911a5eb1..f615befb4 100644 --- a/sources/shiboken2/ApiExtractor/tests/testmodifydocumentation.cpp +++ b/sources/shiboken2/ApiExtractor/tests/testmodifydocumentation.cpp @@ -60,7 +60,7 @@ R"(<typesystem package="Foo"> QCOMPARE(docMods[1].code().trimmed(), QLatin1String("<para>Some changed contents here</para>")); QCOMPARE(docMods[1].signature(), QString()); QtDocParser docParser; - docParser.setDocumentationDataDirectory(QDir::currentPath()); + docParser.setDocumentationDataDirectory(QLatin1String(":")); docParser.fillDocumentation(classA); const QString actualDocSimplified = classA->documentation().value().simplified(); diff --git a/sources/shiboken2/ApiExtractor/tests/testmodifydocumentation.qrc b/sources/shiboken2/ApiExtractor/tests/testmodifydocumentation.qrc new file mode 100644 index 000000000..76b1bfc61 --- /dev/null +++ b/sources/shiboken2/ApiExtractor/tests/testmodifydocumentation.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource> + <file>a.xml</file> + </qresource> +</RCC> diff --git a/sources/shiboken2/ApiExtractor/tests/testmodifyfunction.cpp b/sources/shiboken2/ApiExtractor/tests/testmodifyfunction.cpp index d0a0c9c7a..af24689fe 100644 --- a/sources/shiboken2/ApiExtractor/tests/testmodifyfunction.cpp +++ b/sources/shiboken2/ApiExtractor/tests/testmodifyfunction.cpp @@ -136,7 +136,8 @@ void TestModifyFunction::invalidateAfterUse() </object-type>\n\ <object-type name='E' />\n\ </typesystem>\n"; - QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false, "0.1")); + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, + false, QLatin1String("0.1"))); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); const AbstractMetaClass *classB = AbstractMetaClass::findClass(classes, QLatin1String("B")); @@ -208,7 +209,8 @@ void TestModifyFunction::testWithApiVersion() </modify-function>\n\ </object-type>\n\ </typesystem>\n"; - QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false, "0.1")); + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, + false, QLatin1String("0.1"))); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); AbstractMetaClass* classB = AbstractMetaClass::findClass(classes, QLatin1String("B")); @@ -220,6 +222,61 @@ void TestModifyFunction::testWithApiVersion() QVERIFY(func->ownership(func->ownerClass(), TypeSystem::TargetLangCode, 0) != TypeSystem::CppOwnership); } +void TestModifyFunction::testAllowThread() +{ + const char cppCode[] =R"CPP(\ +struct A { + void f1(); + void f2(); + void f3(); + int getter1() const; + int getter2() const; +}; +)CPP"; + + const char xmlCode[] = R"XML( +<typesystem package='Foo'> + <primitive-type name='int'/> + <object-type name='A'> + <modify-function signature='f2()' allow-thread='auto'/> + <modify-function signature='f3()' allow-thread='no'/> + <modify-function signature='getter2()const' allow-thread='yes'/> + </object-type> +</typesystem> +)XML"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, + false, QLatin1String("0.1"))); + QVERIFY(!builder.isNull()); + AbstractMetaClassList classes = builder->classes(); + const AbstractMetaClass *classA = AbstractMetaClass::findClass(classes, QLatin1String("A")); + QVERIFY(classA); + + // Nothing specified, true + const AbstractMetaFunction *f1 = classA->findFunction(QLatin1String("f1")); + QVERIFY(f1); + QVERIFY(f1->allowThread()); + + // 'auto' specified, should be true for nontrivial function + const AbstractMetaFunction *f2 = classA->findFunction(QLatin1String("f2")); + QVERIFY(f2); + QVERIFY(f2->allowThread()); + + // 'no' specified, should be false + const AbstractMetaFunction *f3 = classA->findFunction(QLatin1String("f3")); + QVERIFY(f3); + QVERIFY(!f3->allowThread()); + + // Nothing specified, should be false for simple getter + const AbstractMetaFunction *getter1 = classA->findFunction(QLatin1String("getter1")); + QVERIFY(getter1); + QVERIFY(!getter1->allowThread()); + + // Forced to true simple getter + const AbstractMetaFunction *getter2 = classA->findFunction(QLatin1String("getter2")); + QVERIFY(getter2); + QVERIFY(getter2->allowThread()); // Forced to true simple getter +} + void TestModifyFunction::testGlobalFunctionModification() { const char* cppCode ="\ @@ -258,4 +315,42 @@ void TestModifyFunction::testGlobalFunctionModification() QCOMPARE(arg->defaultValueExpression(), QLatin1String("A()")); } +void TestModifyFunction::testExceptionSpecification() +{ + const char cppCode[] = R"CPP( +struct A { + void unspecified(); + void nonThrowing() noexcept; + void throwing() throw(int); +}; +)CPP"; + const char xmlCode[] = R"XML( +<typesystem package="Foo"> + <primitive-type name='int'/> + <object-type name='A'> + <modify-function signature='throwing()' exception-handling='auto-on'/> + </object-type> +</typesystem>)XML"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(!builder.isNull()); + + const AbstractMetaClass *classA = AbstractMetaClass::findClass(builder->classes(), QLatin1String("A")); + QVERIFY(classA); + + const AbstractMetaFunction *f = classA->findFunction(QStringLiteral("unspecified")); + QVERIFY(f); + QCOMPARE(f->exceptionSpecification(), ExceptionSpecification::Unknown); + QVERIFY(!f->generateExceptionHandling()); + + f = classA->findFunction(QStringLiteral("nonThrowing")); + QVERIFY(f); + QCOMPARE(f->exceptionSpecification(), ExceptionSpecification::NoExcept); + QVERIFY(!f->generateExceptionHandling()); + + f = classA->findFunction(QStringLiteral("throwing")); + QVERIFY(f); + QCOMPARE(f->exceptionSpecification(), ExceptionSpecification::Throws); + QVERIFY(f->generateExceptionHandling()); +} + QTEST_APPLESS_MAIN(TestModifyFunction) diff --git a/sources/shiboken2/ApiExtractor/tests/testmodifyfunction.h b/sources/shiboken2/ApiExtractor/tests/testmodifyfunction.h index f116b5124..494f31991 100644 --- a/sources/shiboken2/ApiExtractor/tests/testmodifyfunction.h +++ b/sources/shiboken2/ApiExtractor/tests/testmodifyfunction.h @@ -37,10 +37,12 @@ class TestModifyFunction : public QObject private slots: void testOwnershipTransfer(); void testWithApiVersion(); + void testAllowThread(); void testRenameArgument_data(); void testRenameArgument(); void invalidateAfterUse(); void testGlobalFunctionModification(); + void testExceptionSpecification(); }; #endif diff --git a/sources/shiboken2/ApiExtractor/tests/testrefcounttag.cpp b/sources/shiboken2/ApiExtractor/tests/testrefcounttag.cpp index 11cda3317..38099c455 100644 --- a/sources/shiboken2/ApiExtractor/tests/testrefcounttag.cpp +++ b/sources/shiboken2/ApiExtractor/tests/testrefcounttag.cpp @@ -82,7 +82,8 @@ void TestRefCountTag::testWithApiVersion() </object-type>\n\ </typesystem>\n"; - QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false, "0.1")); + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, + false, QLatin1String("0.1"))); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); const AbstractMetaClass *classB = AbstractMetaClass::findClass(classes, QLatin1String("B")); diff --git a/sources/shiboken2/ApiExtractor/tests/testtemplates.cpp b/sources/shiboken2/ApiExtractor/tests/testtemplates.cpp index 8d869e3f9..b1b171bae 100644 --- a/sources/shiboken2/ApiExtractor/tests/testtemplates.cpp +++ b/sources/shiboken2/ApiExtractor/tests/testtemplates.cpp @@ -28,6 +28,7 @@ #include "testtemplates.h" #include <QtTest/QTest> +#include <QtCore/QTextStream> #include <QTemporaryFile> #include "testutil.h" #include <abstractmetalang.h> @@ -438,4 +439,122 @@ typedef Vector<int> IntVector; QCOMPARE(otherMethod->type()->cppSignature(), QLatin1String("Vector<int >")); } +// Perform checks on template inheritance; a typedef of a template class +// should result in rewritten types. +void TestTemplates::testTemplateTypeDefs_data() +{ + QTest::addColumn<QString>("cpp"); + QTest::addColumn<QString>("xml"); + + const char optionalClassDef[] = R"CPP( +template<class T> // Some value type similar to std::optional +class Optional { +public: + T value() const { return m_value; } + operator bool() const { return m_success; } + + T m_value; + bool m_success = false; +}; +)CPP"; + + const char xmlPrefix[] = R"XML( +<typesystem package='Foo'> + <primitive-type name='int'/> + <primitive-type name='bool'/> +)XML"; + + const char xmlOptionalDecl[] = "<value-type name='Optional' generate='no'/>\n"; + const char xmlOptionalIntDecl[] = "<value-type name='IntOptional'/>\n"; + const char xmlPostFix[] = "</typesystem>\n"; + + // Flat, global namespace + QString cpp; + QTextStream(&cpp) << optionalClassDef + << "typedef Optional<int> IntOptional;\n"; + QString xml; + QTextStream(&xml) << xmlPrefix << xmlOptionalDecl << xmlOptionalIntDecl + << "<typedef-type name='XmlIntOptional' source='Optional<int>'/>" + << xmlPostFix; + QTest::newRow("global-namespace") + << cpp << xml; + + // Typedef from namespace Std + cpp.clear(); + QTextStream(&cpp) << "namespace Std {\n" << optionalClassDef << "}\n" + << "typedef Std::Optional<int> IntOptional;\n"; + xml.clear(); + QTextStream(&xml) << xmlPrefix + << "<namespace-type name='Std'>\n" << xmlOptionalDecl + << "</namespace-type>\n" << xmlOptionalIntDecl + << "<typedef-type name='XmlIntOptional' source='Std::Optional<int>'/>" + << xmlPostFix; + QTest::newRow("namespace-Std") + << cpp << xml; + + // Typedef from nested class + cpp.clear(); + QTextStream(&cpp) << "class Outer {\npublic:\n" << optionalClassDef << "\n};\n" + << "typedef Outer::Optional<int> IntOptional;\n"; + xml.clear(); + QTextStream(&xml) << xmlPrefix + << "<object-type name='Outer'>\n" << xmlOptionalDecl + << "</object-type>\n" << xmlOptionalIntDecl + << "<typedef-type name='XmlIntOptional' source='Outer::Optional<int>'/>" + << xmlPostFix; + QTest::newRow("nested-class") + << cpp << xml; +} + +void TestTemplates::testTemplateTypeDefs() +{ + QFETCH(QString, cpp); + QFETCH(QString, xml); + + const QByteArray cppBa = cpp.toLocal8Bit(); + const QByteArray xmlBa = xml.toLocal8Bit(); + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppBa.constData(), xmlBa.constData(), true)); + QVERIFY(!builder.isNull()); + AbstractMetaClassList classes = builder->classes(); + + const AbstractMetaClass *optional = AbstractMetaClass::findClass(classes, QLatin1String("Optional")); + QVERIFY(optional); + + // Find the typedef'ed class + const AbstractMetaClass *optionalInt = + AbstractMetaClass::findClass(classes, QLatin1String("IntOptional")); + QVERIFY(optionalInt); + QCOMPARE(optionalInt->templateBaseClass(), optional); + + // Find the class typedef'ed in the typesystem XML + const AbstractMetaClass *xmlOptionalInt = + AbstractMetaClass::findClass(classes, QLatin1String("XmlIntOptional")); + QVERIFY(xmlOptionalInt); + QCOMPARE(xmlOptionalInt->templateBaseClass(), optional); + + // Check whether the value() method now has an 'int' return + const AbstractMetaFunction *valueMethod = + optionalInt->findFunction(QLatin1String("value")); + QVERIFY(valueMethod); + QCOMPARE(valueMethod->type()->cppSignature(), QLatin1String("int")); + + // ditto for typesystem XML + const AbstractMetaFunction *xmlValueMethod = + xmlOptionalInt->findFunction(QLatin1String("value")); + QVERIFY(xmlValueMethod); + QCOMPARE(xmlValueMethod->type()->cppSignature(), QLatin1String("int")); + + // Check whether the m_value field is of type 'int' + const AbstractMetaField *valueField = + optionalInt->findField(QLatin1String("m_value")); + QVERIFY(valueField); + QCOMPARE(valueField->type()->cppSignature(), QLatin1String("int")); + + // ditto for typesystem XML + const AbstractMetaField *xmlValueField = + xmlOptionalInt->findField(QLatin1String("m_value")); + QVERIFY(xmlValueField); + QCOMPARE(xmlValueField->type()->cppSignature(), QLatin1String("int")); +} + QTEST_APPLESS_MAIN(TestTemplates) diff --git a/sources/shiboken2/ApiExtractor/tests/testtemplates.h b/sources/shiboken2/ApiExtractor/tests/testtemplates.h index 3e1565933..df3de18b9 100644 --- a/sources/shiboken2/ApiExtractor/tests/testtemplates.h +++ b/sources/shiboken2/ApiExtractor/tests/testtemplates.h @@ -46,6 +46,8 @@ private slots: void testTemplateInheritanceMixedWithNamespaceAndForwardDeclaration(); void testTypedefOfInstantiationOfTemplateClass(); void testContainerTypeIncompleteArgument(); + void testTemplateTypeDefs_data(); + void testTemplateTypeDefs(); }; #endif diff --git a/sources/shiboken2/ApiExtractor/tests/testtyperevision.cpp b/sources/shiboken2/ApiExtractor/tests/testtyperevision.cpp index 1ec7ce025..a7e88e437 100644 --- a/sources/shiboken2/ApiExtractor/tests/testtyperevision.cpp +++ b/sources/shiboken2/ApiExtractor/tests/testtyperevision.cpp @@ -31,6 +31,7 @@ #include "testutil.h" #include <abstractmetalang.h> #include <typesystem.h> +#include <typedatabase.h> void TestTypeRevision::testRevisionAttr() { @@ -49,21 +50,55 @@ void TestTypeRevision::testRevisionAttr() QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); const AbstractMetaClass *rev0 = AbstractMetaClass::findClass(classes, QLatin1String("Rev_0")); - QCOMPARE(getTypeRevision(rev0->typeEntry()), 0); + QCOMPARE(rev0->typeEntry()->revision(), 0); const AbstractMetaClass *rev1 = AbstractMetaClass::findClass(classes, QLatin1String("Rev_1")); - QCOMPARE(getTypeRevision(rev1->typeEntry()), 1); + QCOMPARE(rev1->typeEntry()->revision(), 1); AbstractMetaClass *rev2 = AbstractMetaClass::findClass(classes, QLatin1String("Rev_2")); - QCOMPARE(getTypeRevision(rev2->typeEntry()), 2); + QCOMPARE(rev2->typeEntry()->revision(), 2); AbstractMetaEnum* rev3 = rev2->findEnum(QLatin1String("Rev_3")); - QCOMPARE(getTypeRevision(rev3->typeEntry()), 3); + QCOMPARE(rev3->typeEntry()->revision(), 3); FlagsTypeEntry* rev4 = rev3->typeEntry()->flags(); - QCOMPARE(getTypeRevision(rev4), 4); + QCOMPARE(rev4->revision(), 4); AbstractMetaEnum* rev5 = rev2->findEnum(QLatin1String("Rev_5")); - QCOMPARE(getTypeRevision(rev5->typeEntry()), 5); - QCOMPARE(getTypeRevision(rev5->typeEntry()->flags()), 5); + const EnumTypeEntry *revEnumTypeEntry = rev5->typeEntry(); + QCOMPARE(revEnumTypeEntry->revision(), 5); + QCOMPARE(revEnumTypeEntry->flags()->revision(), 5); +} + + +void TestTypeRevision::testVersion_data() +{ + QTest::addColumn<QString>("version"); + QTest::addColumn<int>("expectedClassCount"); + + QTest::newRow("none") << QString() << 2; + QTest::newRow("1.0") << QString::fromLatin1("1.0") << 1; // Bar20 excluded + QTest::newRow("2.0") << QString::fromLatin1("2.0") << 2; +} + +void TestTypeRevision::testVersion() +{ + QFETCH(QString, version); + QFETCH(int, expectedClassCount); + + const char cppCode[] = R"CPP( +class Bar {}; +class Bar20 {}; +)CPP"; + const char xmlCode[] = R"XML( +<typesystem package="Foo"> + <value-type name="Bar"/> + <value-type name="Bar20" since="2.0"/> +</typesystem> +)XML"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, true, version)); + QVERIFY(!builder.isNull()); + + QCOMPARE(builder->classes().size(), expectedClassCount); } QTEST_APPLESS_MAIN(TestTypeRevision) diff --git a/sources/shiboken2/ApiExtractor/tests/testtyperevision.h b/sources/shiboken2/ApiExtractor/tests/testtyperevision.h index 4dfa241e3..3832c3883 100644 --- a/sources/shiboken2/ApiExtractor/tests/testtyperevision.h +++ b/sources/shiboken2/ApiExtractor/tests/testtyperevision.h @@ -37,6 +37,8 @@ class TestTypeRevision : public QObject private slots: void testRevisionAttr(); + void testVersion_data(); + void testVersion(); }; #endif diff --git a/sources/shiboken2/ApiExtractor/tests/testutil.h b/sources/shiboken2/ApiExtractor/tests/testutil.h index 6152793f5..c6ad19d7e 100644 --- a/sources/shiboken2/ApiExtractor/tests/testutil.h +++ b/sources/shiboken2/ApiExtractor/tests/testutil.h @@ -40,20 +40,23 @@ namespace TestUtil { static AbstractMetaBuilder *parse(const char *cppCode, const char *xmlCode, bool silent = true, - const char *apiVersion = Q_NULLPTR, + const QString &apiVersion = QString(), const QStringList &dropTypeEntries = QStringList()) { ReportHandler::setSilent(silent); TypeDatabase* td = TypeDatabase::instance(true); - if (apiVersion && !td->setApiVersion(QLatin1String("*"), QLatin1String(apiVersion))) - return Q_NULLPTR; + if (apiVersion.isEmpty()) + TypeDatabase::clearApiVersions(); + else if (!td->setApiVersion(QLatin1String("*"), apiVersion)) + return nullptr; td->setDropTypeEntries(dropTypeEntries); QBuffer buffer; // parse typesystem buffer.setData(xmlCode); if (!buffer.open(QIODevice::ReadOnly)) return Q_NULLPTR; - td->parseFile(&buffer); + if (!td->parseFile(&buffer)) + return nullptr; buffer.close(); // parse C++ code QTemporaryFile tempSource(QDir::tempPath() + QLatin1String("/st_XXXXXX_main.cpp")); diff --git a/sources/shiboken2/ApiExtractor/typedatabase.cpp b/sources/shiboken2/ApiExtractor/typedatabase.cpp index 9529de40a..4cb6cdd8a 100644 --- a/sources/shiboken2/ApiExtractor/typedatabase.cpp +++ b/sources/shiboken2/ApiExtractor/typedatabase.cpp @@ -166,64 +166,66 @@ ContainerTypeEntry* TypeDatabase::findContainerType(const QString &name) const return 0; } -FunctionTypeEntry* TypeDatabase::findFunctionType(const QString& name) const +static bool inline useType(const TypeEntry *t) { - TypeEntry* entry = findType(name); - if (entry && entry->type() == TypeEntry::FunctionType) - return static_cast<FunctionTypeEntry*>(entry); - return 0; + return !t->isPrimitive() + || static_cast<const PrimitiveTypeEntry *>(t)->preferredTargetLangType(); } -TypeEntry* TypeDatabase::findType(const QString& name) const +FunctionTypeEntry* TypeDatabase::findFunctionType(const QString& name) const { - const TypeEntryList &entries = findTypes(name); + const auto entries = findTypes(name); for (TypeEntry *entry : entries) { - if (entry && - (!entry->isPrimitive() || static_cast<PrimitiveTypeEntry *>(entry)->preferredTargetLangType())) { - return entry; - } + if (entry->type() == TypeEntry::FunctionType && useType(entry)) + return static_cast<FunctionTypeEntry*>(entry); } return 0; } -TypeEntryList TypeDatabase::findTypes(const QString &name) const +const TypeSystemTypeEntry *TypeDatabase::findTypeSystemType(const QString &name) const { - return m_entries.value(name); + const auto entries = findTypes(name); + for (const TypeEntry *entry : entries) { + if (entry->type() == TypeEntry::TypeSystemType) + return static_cast<const TypeSystemTypeEntry *>(entry); + } + return nullptr; } -SingleTypeEntryHash TypeDatabase::entries() const +TypeEntry* TypeDatabase::findType(const QString& name) const { - TypeEntryHash entries = allEntries(); - - SingleTypeEntryHash returned; - for (TypeEntryHash::const_iterator it = entries.cbegin(), end = entries.cend(); it != end; ++it) - returned.insert(it.key(), findType(it.key())); + const auto entries = findTypes(name); + for (TypeEntry *entry : entries) { + if (useType(entry)) + return entry; + } + return nullptr; +} - return returned; +TypeEntryMultiMapConstIteratorRange TypeDatabase::findTypes(const QString &name) const +{ + const auto range = m_entries.equal_range(name); + return {range.first, range.second}; } PrimitiveTypeEntryList TypeDatabase::primitiveTypes() const { - TypeEntryHash entries = allEntries(); PrimitiveTypeEntryList returned; - for (TypeEntryHash::const_iterator it = entries.cbegin(), end = entries.cend(); it != end; ++it) { - for (TypeEntry *typeEntry : it.value()) { - if (typeEntry->isPrimitive()) - returned.append(static_cast<PrimitiveTypeEntry *>(typeEntry)); - } + for (auto it = m_entries.cbegin(), end = m_entries.cend(); it != end; ++it) { + TypeEntry *typeEntry = it.value(); + if (typeEntry->isPrimitive()) + returned.append(static_cast<PrimitiveTypeEntry *>(typeEntry)); } return returned; } ContainerTypeEntryList TypeDatabase::containerTypes() const { - TypeEntryHash entries = allEntries(); ContainerTypeEntryList returned; - for (TypeEntryHash::const_iterator it = entries.cbegin(), end = entries.cend(); it != end; ++it) { - for (TypeEntry *typeEntry : it.value()) { - if (typeEntry->isContainer()) - returned.append(static_cast<ContainerTypeEntry *>(typeEntry)); - } + for (auto it = m_entries.cbegin(), end = m_entries.cend(); it != end; ++it) { + TypeEntry *typeEntry = it.value(); + if (typeEntry->isContainer()) + returned.append(static_cast<ContainerTypeEntry *>(typeEntry)); } return returned; } @@ -263,6 +265,8 @@ static inline QString msgRejectReason(const TypeRejection &r, const QString &nee str << " matches class \"" << r.className.pattern() << "\" and \"" << needle << "\" matches \"" << r.pattern.pattern() << '"'; break; + case TypeRejection::Invalid: + break; } return result; } @@ -303,9 +307,52 @@ bool TypeDatabase::isEnumRejected(const QString& className, const QString& enumN return findRejection(m_rejections, TypeRejection::Enum, className, enumName, reason); } -void TypeDatabase::addType(TypeEntry *e) +TypeEntry *TypeDatabase::resolveTypeDefEntry(TypedefEntry *typedefEntry, + QString *errorMessage) +{ + QString sourceName = typedefEntry->sourceType(); + const int lessThanPos = sourceName.indexOf(QLatin1Char('<')); + if (lessThanPos != -1) + sourceName.truncate(lessThanPos); + ComplexTypeEntry *source = nullptr; + for (TypeEntry *e : findTypes(sourceName)) { + switch (e->type()) { + case TypeEntry::BasicValueType: + case TypeEntry::ContainerType: + case TypeEntry::InterfaceType: + case TypeEntry::ObjectType: + case TypeEntry::SmartPointerType: + source = dynamic_cast<ComplexTypeEntry *>(e); + Q_ASSERT(source); + break; + default: + break; + } + } + if (!source) { + if (errorMessage) + *errorMessage = QLatin1String("Unable to resolve typedef \"") + + typedefEntry->sourceType() + QLatin1Char('"'); + return nullptr; + } + + ComplexTypeEntry *result = static_cast<ComplexTypeEntry *>(source->clone()); + result->useAsTypedef(typedefEntry); + typedefEntry->setSource(source); + typedefEntry->setTarget(result); + m_typedefEntries.insert(typedefEntry->qualifiedCppName(), typedefEntry); + return result; +} + +bool TypeDatabase::addType(TypeEntry *e, QString *errorMessage) { - m_entries[e->qualifiedCppName()].append(e); + if (e->type() == TypeEntry::TypedefType) { + e = resolveTypeDefEntry(static_cast<TypedefEntry *>(e), errorMessage); + if (Q_UNLIKELY(!e)) + return false; + } + m_entries.insert(e->qualifiedCppName(), e); + return true; } bool TypeDatabase::isFunctionRejected(const QString& className, const QString& functionName, @@ -339,7 +386,7 @@ FlagsTypeEntry* TypeDatabase::findFlagsType(const QString &name) const fte = m_flagsEntries.value(name); if (!fte) { //last hope, search for flag without scope inside of flags hash - for (SingleTypeEntryHash::const_iterator it = m_flagsEntries.cbegin(), end = m_flagsEntries.cend(); it != end; ++it) { + for (auto it = m_flagsEntries.cbegin(), end = m_flagsEntries.cend(); it != end; ++it) { if (it.key().endsWith(name)) { fte = it.value(); break; @@ -427,12 +474,13 @@ bool TypeDatabase::addSuppressedWarning(const QString &warning, QString *errorMe pattern.append(QLatin1Char('$')); } - const QRegularExpression expression(pattern); + QRegularExpression expression(pattern); if (!expression.isValid()) { *errorMessage = QLatin1String("Invalid message pattern \"") + warning + QLatin1String("\": ") + expression.errorString(); return false; } + expression.setPatternOptions(expression.patternOptions() | QRegularExpression::MultilineOption); m_suppressedWarnings.append(expression); return true; @@ -518,16 +566,21 @@ bool TypeDatabase::parseFile(QIODevice* device, bool generate) { QXmlStreamReader reader(device); Handler handler(this, generate); - return handler.parse(reader); + const bool result = handler.parse(reader); + if (!result) + qCWarning(lcShiboken, "%s", qPrintable(handler.errorString())); + return result; } PrimitiveTypeEntry *TypeDatabase::findPrimitiveType(const QString& name) const { - const TypeEntryList &entries = findTypes(name); - + const auto entries = findTypes(name); for (TypeEntry *entry : entries) { - if (entry && entry->isPrimitive() && static_cast<PrimitiveTypeEntry*>(entry)->preferredTargetLangType()) - return static_cast<PrimitiveTypeEntry*>(entry); + if (entry->isPrimitive()) { + PrimitiveTypeEntry *pe = static_cast<PrimitiveTypeEntry *>(entry); + if (pe->preferredTargetLangType()) + return pe; + } } return 0; @@ -535,9 +588,9 @@ PrimitiveTypeEntry *TypeDatabase::findPrimitiveType(const QString& name) const ComplexTypeEntry* TypeDatabase::findComplexType(const QString& name) const { - const TypeEntryList &entries = findTypes(name); + const auto entries = findTypes(name); for (TypeEntry *entry : entries) { - if (entry && entry->isComplex()) + if (entry->isComplex() && useType(entry)) return static_cast<ComplexTypeEntry*>(entry); } return 0; @@ -545,9 +598,9 @@ ComplexTypeEntry* TypeDatabase::findComplexType(const QString& name) const ObjectTypeEntry* TypeDatabase::findObjectType(const QString& name) const { - const TypeEntryList &entries = findTypes(name); + const auto entries = findTypes(name); for (TypeEntry *entry : entries) { - if (entry && entry->isObject()) + if (entry && entry->isObject() && useType(entry)) return static_cast<ObjectTypeEntry*>(entry); } return 0; @@ -555,9 +608,9 @@ ObjectTypeEntry* TypeDatabase::findObjectType(const QString& name) const NamespaceTypeEntry* TypeDatabase::findNamespaceType(const QString& name) const { - const TypeEntryList &entries = findTypes(name); + const auto entries = findTypes(name); for (TypeEntry *entry : entries) { - if (entry && entry->isNamespace()) + if (entry->isNamespace() && useType(entry)) return static_cast<NamespaceTypeEntry*>(entry); } return 0; @@ -574,75 +627,64 @@ void TypeDatabase::setDropTypeEntries(QStringList dropTypeEntries) m_dropTypeEntries.sort(); } -// Using std::pair to save some memory -// the pair means (revision, typeIndex) -// This global variable exists only because we can't break the ABI -typedef QHash<const TypeEntry*, std::pair<int, int> > TypeRevisionMap; -Q_GLOBAL_STATIC(TypeRevisionMap, typeEntryFields); static bool computeTypeIndexes = true; static int maxTypeIndex; -int getTypeRevision(const TypeEntry* typeEntry) +static bool typeEntryLessThan(const TypeEntry* t1, const TypeEntry* t2) { - return typeEntryFields()->value(typeEntry).first; -} - -void setTypeRevision(TypeEntry* typeEntry, int revision) -{ - (*typeEntryFields())[typeEntry].first = revision; - computeTypeIndexes = true; -} - -static bool compareTypeEntriesByName(const TypeEntry* t1, const TypeEntry* t2) -{ - return t1->qualifiedCppName() < t2->qualifiedCppName(); + if (t1->revision() < t2->revision()) + return true; + return t1->revision() == t2->revision() + && t1->qualifiedCppName() < t2->qualifiedCppName(); } static void _computeTypeIndexes() { TypeDatabase* tdb = TypeDatabase::instance(); - typedef QMap<int, TypeEntryList> GroupedTypeEntries; - GroupedTypeEntries groupedEntries; + + TypeEntryList list; // Group type entries by revision numbers - const TypeEntryHash &allEntries = tdb->allEntries(); - for (TypeEntryHash::const_iterator tit = allEntries.cbegin(), end = allEntries.cend(); tit != end; ++tit) { - for (TypeEntry *entry : tit.value()) { - if (entry->isPrimitive() - || entry->isContainer() - || entry->isFunction() - || !entry->generateCode() - || entry->isEnumValue() - || entry->isVarargs() - || entry->isTypeSystem() - || entry->isVoid() - || entry->isCustom()) - continue; - groupedEntries[getTypeRevision(entry)] << entry; - } - } + const auto &allEntries = tdb->entries(); + list.reserve(allEntries.size()); + for (auto tit = allEntries.cbegin(), end = allEntries.cend(); tit != end; ++tit) { + TypeEntry *entry = tit.value(); + if (entry->isPrimitive() + || entry->isContainer() + || entry->isFunction() + || !entry->generateCode() + || entry->isEnumValue() + || entry->isVarargs() + || entry->isTypeSystem() + || entry->isVoid() + || entry->isCustom()) + continue; + if (!list.contains(entry)) // Remove duplicates + list.append(entry); + } + + // Sort the type entries by revision, name + std::sort(list.begin(), list.end(), typeEntryLessThan); maxTypeIndex = 0; - GroupedTypeEntries::iterator it = groupedEntries.begin(); - for (; it != groupedEntries.end(); ++it) { - // Remove duplicates - TypeEntryList::iterator newEnd = std::unique(it.value().begin(), it.value().end()); - it.value().erase(newEnd, it.value().end()); - // Sort the type entries by name - qSort(it.value().begin(), newEnd, compareTypeEntriesByName); - - for (TypeEntry *entry : qAsConst(it.value())) { - (*typeEntryFields())[entry].second = maxTypeIndex++; - } - } + for (TypeEntry *e : qAsConst(list)) + e->setSbkIndex(maxTypeIndex++); computeTypeIndexes = false; } -int getTypeIndex(const TypeEntry* typeEntry) +void TypeEntry::setRevision(int r) +{ + if (m_revision != r) { + m_revision = r; + computeTypeIndexes = true; + } +} + +int TypeEntry::sbkIndex() const { if (computeTypeIndexes) _computeTypeIndexes(); - return typeEntryFields()->value(typeEntry).second; + return m_sbkIndex; } int getMaxTypeIndex() @@ -652,6 +694,11 @@ int getMaxTypeIndex() return maxTypeIndex; } +void TypeDatabase::clearApiVersions() +{ + apiVersions()->clear(); +} + bool TypeDatabase::setApiVersion(const QString& packageWildcardPattern, const QString &version) { const QString packagePattern = wildcardToRegExp(packageWildcardPattern.trimmed()); @@ -673,9 +720,11 @@ bool TypeDatabase::setApiVersion(const QString& packageWildcardPattern, const QS } bool TypeDatabase::checkApiVersion(const QString &package, - const QVersionNumber &versionNumber) const + const QVersionNumber &versionNumber) { const ApiVersions &versions = *apiVersions(); + if (versions.isEmpty()) // Nothing specified: use latest. + return true; for (int i = 0, size = versions.size(); i < size; ++i) { if (versions.at(i).first.match(package).hasMatch()) return versions.at(i).second >= versionNumber; @@ -684,33 +733,104 @@ bool TypeDatabase::checkApiVersion(const QString &package, } #ifndef QT_NO_DEBUG_STREAM + +#define FORMAT_BOOL(name, var) \ + if (var) \ + d << ", [" << name << ']'; + +#define FORMAT_NONEMPTY_STRING(name, var) \ + if (!var.isEmpty()) \ + d << ", " << name << "=\"" << var << '"'; + +#define FORMAT_LIST_SIZE(name, var) \ + if (!var.isEmpty()) \ + d << ", " << var.size() << ' ' << name; + +void TypeEntry::formatDebug(QDebug &d) const +{ + const QString cppName = qualifiedCppName(); + d << '"' << m_name << '"'; + if (m_name != cppName) + d << "\", cppName=\"" << cppName << '"'; + d << ", type=" << m_type << ", codeGeneration=0x" + << hex << m_codeGeneration << dec; + FORMAT_NONEMPTY_STRING("package", m_targetLangPackage) + FORMAT_BOOL("stream", m_stream) + FORMAT_LIST_SIZE("codeSnips", m_codeSnips) + FORMAT_NONEMPTY_STRING("conversionRule", m_conversionRule) + if (!m_version.isNull() && m_version > QVersionNumber(0, 0)) + d << ", version=" << m_version; + if (m_revision) + d << ", revision=" << m_revision; + if (m_sbkIndex) + d << ", sbkIndex=" << m_sbkIndex; + if (m_include.isValid()) + d << ", include=" << m_include; + if (const int count = m_extraIncludes.size()) { + d << ", extraIncludes[" << count << "]="; + for (int i = 0; i < count; ++i) { + if (i) + d << ", "; + d << m_extraIncludes.at(i); + } + } +} + +void ComplexTypeEntry::formatDebug(QDebug &d) const +{ + TypeEntry::formatDebug(d); + FORMAT_NONEMPTY_STRING("targetLangName", m_targetLangName) + FORMAT_BOOL("QObject", m_qobject) + FORMAT_BOOL("polymorphicBase", m_polymorphicBase) + FORMAT_BOOL("genericClass", m_genericClass) + FORMAT_BOOL("deleteInMainThread", m_deleteInMainThread) + if (m_typeFlags != 0) + d << ", typeFlags=" << m_typeFlags; + d << ", copyableFlag=" << m_copyableFlag + << ", except=" << int(m_exceptionHandling); + FORMAT_NONEMPTY_STRING("defaultSuperclass", m_defaultSuperclass) + FORMAT_NONEMPTY_STRING("polymorphicIdValue", m_polymorphicIdValue) + FORMAT_NONEMPTY_STRING("lookupName", m_lookupName) + FORMAT_NONEMPTY_STRING("targetType", m_targetType) + FORMAT_NONEMPTY_STRING("hash", m_hashFunction) + FORMAT_LIST_SIZE("addedFunctions", m_addedFunctions) + FORMAT_LIST_SIZE("functionMods", m_functionMods) + FORMAT_LIST_SIZE("fieldMods", m_fieldMods) +} + +void TypedefEntry::formatDebug(QDebug &d) const +{ + ComplexTypeEntry::formatDebug(d); + d << ", sourceType=\"" << m_sourceType << '"' + << ", source=" << m_source << ", target=" << m_target; +} + +void EnumTypeEntry::formatDebug(QDebug &d) const +{ + TypeEntry::formatDebug(d); + FORMAT_NONEMPTY_STRING("package", m_packageName) + FORMAT_NONEMPTY_STRING("qualifier", m_qualifier) + FORMAT_NONEMPTY_STRING("targetLangName", m_targetLangName) + if (m_flags) + d << ", flags=(" << m_flags << ')'; +} + +void ContainerTypeEntry::formatDebug(QDebug &d) const +{ + ComplexTypeEntry::formatDebug(d); + d << ", type=" << m_type << ",\"" << typeName() << '"'; +} + QDebug operator<<(QDebug d, const TypeEntry *te) { QDebugStateSaver saver(d); d.noquote(); d.nospace(); d << "TypeEntry("; - if (te) { - const QString name = te->name(); - const QString cppName = te->qualifiedCppName(); - d << '"' << name << '"'; - if (name != cppName) - d << "\", cppName=\"" << cppName << '"'; - d << ", type=" << te->type(); - if (te->include().isValid()) - d << ", include=" << te->include(); - const IncludeList &extraIncludes = te->extraIncludes(); - if (const int count = extraIncludes.size()) { - d << ", extraIncludes[" << count << "]="; - for (int i = 0; i < count; ++i) { - if (i) - d << ", "; - d << extraIncludes.at(i); - } - } - } else { + if (te) + te->formatDebug(d); + else d << '0'; - } d << ')'; return d; } @@ -732,25 +852,14 @@ QDebug operator<<(QDebug d, const TemplateEntry *te) void TypeDatabase::formatDebug(QDebug &d) const { - typedef TypeEntryHash::ConstIterator Eit; - typedef SingleTypeEntryHash::ConstIterator Sit; - typedef TemplateEntryHash::ConstIterator TplIt; d << "TypeDatabase(" << "entries[" << m_entries.size() << "]="; - for (Eit it = m_entries.cbegin(), end = m_entries.cend(); it != end; ++it) { - const int count = it.value().size(); - d << '"' << it.key() << "\" [" << count << "]: ("; - for (int t = 0; t < count; ++t) { - if (t) - d << ", "; - d << it.value().at(t); - } - d << ")\n"; - } + for (auto it = m_entries.cbegin(), end = m_entries.cend(); it != end; ++it) + d << " " << it.value() << '\n'; if (!m_templates.isEmpty()) { d << "templates[" << m_templates.size() << "]=("; - const TplIt begin = m_templates.cbegin(); - for (TplIt it = begin, end = m_templates.cend(); it != end; ++it) { + const auto begin = m_templates.cbegin(); + for (auto it = begin, end = m_templates.cend(); it != end; ++it) { if (it != begin) d << ", "; d << it.value(); @@ -759,8 +868,8 @@ void TypeDatabase::formatDebug(QDebug &d) const } if (!m_flagsEntries.isEmpty()) { d << "flags[" << m_flagsEntries.size() << "]=("; - const Sit begin = m_flagsEntries.cbegin(); - for (Sit it = begin, end = m_flagsEntries.cend(); it != end; ++it) { + const auto begin = m_flagsEntries.cbegin(); + for (auto it = begin, end = m_flagsEntries.cend(); it != end; ++it) { if (it != begin) d << ", "; d << it.value(); diff --git a/sources/shiboken2/ApiExtractor/typedatabase.h b/sources/shiboken2/ApiExtractor/typedatabase.h index 2e7b009c2..247d74362 100644 --- a/sources/shiboken2/ApiExtractor/typedatabase.h +++ b/sources/shiboken2/ApiExtractor/typedatabase.h @@ -54,13 +54,12 @@ struct TypeRejection; QT_FORWARD_DECLARE_CLASS(QDebug) -void setTypeRevision(TypeEntry* typeEntry, int revision); -int getTypeRevision(const TypeEntry* typeEntry); -int getTypeIndex(const TypeEntry* typeEntry); int getMaxTypeIndex(); class ContainerTypeEntry; class PrimitiveTypeEntry; +class TypeSystemTypeEntry; + class TypeDatabase { TypeDatabase(); @@ -91,12 +90,12 @@ public: NamespaceTypeEntry* findNamespaceType(const QString& name) const; ContainerTypeEntry* findContainerType(const QString& name) const; FunctionTypeEntry* findFunctionType(const QString& name) const; + const TypeSystemTypeEntry *findTypeSystemType(const QString &name) const; TypeEntry* findType(const QString& name) const; - TypeEntryHash allEntries() const { return m_entries; } - - SingleTypeEntryHash entries() const; + const TypeEntryMultiMap &entries() const { return m_entries; } + const TypedefEntryMap &typedefEntries() const { return m_typedefEntries; } PrimitiveTypeEntryList primitiveTypes() const; @@ -115,7 +114,7 @@ public: bool isReturnTypeRejected(const QString& className, const QString& typeName, QString *reason = nullptr) const; - void addType(TypeEntry* e); + bool addType(TypeEntry* e, QString *errorMessage = nullptr); FlagsTypeEntry* findFlagsType(const QString& name) const; void addFlagsType(FlagsTypeEntry* fte); @@ -147,9 +146,10 @@ public: bool parseFile(QIODevice* device, bool generate = true); - bool setApiVersion(const QString& package, const QString& version); + static bool setApiVersion(const QString& package, const QString& version); + static void clearApiVersions(); - bool checkApiVersion(const QString &package, const QVersionNumber &version) const; + static bool checkApiVersion(const QString &package, const QVersionNumber &version); bool hasDroppedTypeEntries() const { return !m_dropTypeEntries.isEmpty(); } @@ -163,12 +163,14 @@ public: void formatDebug(QDebug &d) const; #endif private: - TypeEntryList findTypes(const QString &name) const; + TypeEntryMultiMapConstIteratorRange findTypes(const QString &name) const; + TypeEntry *resolveTypeDefEntry(TypedefEntry *typedefEntry, QString *errorMessage); bool m_suppressWarnings; - TypeEntryHash m_entries; - SingleTypeEntryHash m_flagsEntries; - TemplateEntryHash m_templates; + TypeEntryMultiMap m_entries; + TypeEntryMap m_flagsEntries; + TypedefEntryMap m_typedefEntries; + TemplateEntryMap m_templates; QVector<QRegularExpression> m_suppressedWarnings; AddedFunctionList m_globalUserFunctions; diff --git a/sources/shiboken2/ApiExtractor/typedatabase_typedefs.h b/sources/shiboken2/ApiExtractor/typedatabase_typedefs.h index 083602322..fbbbabe43 100644 --- a/sources/shiboken2/ApiExtractor/typedatabase_typedefs.h +++ b/sources/shiboken2/ApiExtractor/typedatabase_typedefs.h @@ -29,7 +29,7 @@ #ifndef TYPEDATABASE_TYPEDEFS_H #define TYPEDATABASE_TYPEDEFS_H -#include <QtCore/QHash> +#include <QtCore/QMultiMap> #include <QtCore/QString> #include <QtCore/QVector> @@ -37,11 +37,28 @@ class ContainerTypeEntry; class PrimitiveTypeEntry; class TemplateEntry; class TypeEntry; +class TypedefEntry; typedef QVector<TypeEntry *> TypeEntryList; -typedef QHash<QString, TypeEntryList> TypeEntryHash; -typedef QHash<QString, TypeEntry *> SingleTypeEntryHash; -typedef QHash<QString, TemplateEntry *> TemplateEntryHash; +typedef QMap<QString, TemplateEntry *> TemplateEntryMap; + +template <class Key, class Value> +struct QMultiMapConstIteratorRange // A range of iterator for a range-based for loop +{ + using ConstIterator = typename QMultiMap<Key, Value>::const_iterator; + + ConstIterator begin() const { return m_begin; } + ConstIterator end() const { return m_end; } + + ConstIterator m_begin; + ConstIterator m_end; +}; + +typedef QMultiMap<QString, TypeEntry *> TypeEntryMultiMap; +typedef QMultiMapConstIteratorRange<QString, TypeEntry *> TypeEntryMultiMapConstIteratorRange; + +typedef QMap<QString, TypeEntry *> TypeEntryMap; +typedef QMap<QString, TypedefEntry *> TypedefEntryMap; typedef QVector<const ContainerTypeEntry *> ContainerTypeEntryList; typedef QVector<const PrimitiveTypeEntry *> PrimitiveTypeEntryList; diff --git a/sources/shiboken2/ApiExtractor/typeparser.cpp b/sources/shiboken2/ApiExtractor/typeparser.cpp index 02c85421b..c440fb66d 100644 --- a/sources/shiboken2/ApiExtractor/typeparser.cpp +++ b/sources/shiboken2/ApiExtractor/typeparser.cpp @@ -49,13 +49,14 @@ public: GreaterThanToken, ConstToken, + VolatileToken, Identifier, NoToken, InvalidToken }; Scanner(const QString &s) - : m_pos(0), m_length(s.length()), m_chars(s.constData()) + : m_pos(0), m_length(s.length()), m_tokenStart(-1), m_chars(s.constData()) { } @@ -137,13 +138,30 @@ Scanner::Token Scanner::nextToken(QString *errorMessage) } } - if (tok == Identifier && m_pos - m_tokenStart == 5) { - if (m_chars[m_tokenStart] == QLatin1Char('c') - && m_chars[m_tokenStart + 1] == QLatin1Char('o') - && m_chars[m_tokenStart + 2] == QLatin1Char('n') - && m_chars[m_tokenStart + 3] == QLatin1Char('s') - && m_chars[m_tokenStart + 4] == QLatin1Char('t')) - tok = ConstToken; + if (tok == Identifier) { + switch (m_pos - m_tokenStart) { + case 5: + if (m_chars[m_tokenStart] == QLatin1Char('c') + && m_chars[m_tokenStart + 1] == QLatin1Char('o') + && m_chars[m_tokenStart + 2] == QLatin1Char('n') + && m_chars[m_tokenStart + 3] == QLatin1Char('s') + && m_chars[m_tokenStart + 4] == QLatin1Char('t')) { + tok = ConstToken; + } + break; + case 8: + if (m_chars[m_tokenStart] == QLatin1Char('v') + && m_chars[m_tokenStart + 1] == QLatin1Char('o') + && m_chars[m_tokenStart + 2] == QLatin1Char('l') + && m_chars[m_tokenStart + 3] == QLatin1Char('a') + && m_chars[m_tokenStart + 4] == QLatin1Char('t') + && m_chars[m_tokenStart + 5] == QLatin1Char('i') + && m_chars[m_tokenStart + 6] == QLatin1Char('l') + && m_chars[m_tokenStart + 7] == QLatin1Char('e')) { + tok = VolatileToken; + } + break; + } } return tok; @@ -167,6 +185,7 @@ TypeInfo TypeParser::parse(const QString &str, QString *errorMessage) bool colon_prefix = false; bool in_array = false; QString array; + bool seenStar = false; Scanner::Token tok = scanner.nextToken(errorMessage); while (tok != Scanner::NoToken) { @@ -191,7 +210,8 @@ TypeInfo TypeParser::parse(const QString &str, QString *errorMessage) switch (tok) { case Scanner::StarToken: - ++stack.top()->m_indirections; + seenStar = true; + stack.top()->addIndirection(Indirection::Pointer); break; case Scanner::AmpersandToken: @@ -212,14 +232,14 @@ TypeInfo TypeParser::parse(const QString &str, QString *errorMessage) } break; case Scanner::LessThanToken: - stack.top()->m_arguments << TypeInfo(); - stack.push(&stack.top()->m_arguments.last()); + stack.top()->m_instantiations << TypeInfo(); + stack.push(&stack.top()->m_instantiations.last()); break; case Scanner::CommaToken: stack.pop(); - stack.top()->m_arguments << TypeInfo(); - stack.push(&stack.top()->m_arguments.last()); + stack.top()->m_instantiations << TypeInfo(); + stack.push(&stack.top()->m_instantiations.last()); break; case Scanner::GreaterThanToken: @@ -231,7 +251,16 @@ TypeInfo TypeParser::parse(const QString &str, QString *errorMessage) break; case Scanner::ConstToken: - stack.top()->m_constant = true; + if (seenStar) { // "int *const": Last indirection is const. + Q_ASSERT(!stack.top()->m_indirections.isEmpty()); + *stack.top()->m_indirections.rbegin() = Indirection::ConstPointer; + } else { + stack.top()->m_constant = true; + } + break; + + case Scanner::VolatileToken: + stack.top()->m_volatile = true; break; case Scanner::OpenParenToken: // function pointers not supported diff --git a/sources/shiboken2/ApiExtractor/typesystem.cpp b/sources/shiboken2/ApiExtractor/typesystem.cpp index 8e0e4437a..2c7f5eeaa 100644 --- a/sources/shiboken2/ApiExtractor/typesystem.cpp +++ b/sources/shiboken2/ApiExtractor/typesystem.cpp @@ -35,29 +35,68 @@ #include <QtCore/QFileInfo> #include <QtCore/QRegularExpression> #include <QtCore/QSet> +#include <QtCore/QStringView> +#include <QtCore/QStringAlgorithms> #include <QtCore/QXmlStreamAttributes> #include <QtCore/QXmlStreamReader> +#include <algorithm> + +const char *TARGET_CONVERSION_RULE_FLAG = "0"; +const char *NATIVE_CONVERSION_RULE_FLAG = "1"; + static QString strings_Object = QLatin1String("Object"); static QString strings_String = QLatin1String("String"); static QString strings_char = QLatin1String("char"); static QString strings_jchar = QLatin1String("jchar"); static QString strings_jobject = QLatin1String("jobject"); +static inline QString allowThreadAttribute() { return QStringLiteral("allow-thread"); } static inline QString colonColon() { return QStringLiteral("::"); } +static inline QString copyableAttribute() { return QStringLiteral("copyable"); } +static inline QString accessAttribute() { return QStringLiteral("access"); } +static inline QString actionAttribute() { return QStringLiteral("action"); } static inline QString quoteAfterLineAttribute() { return QStringLiteral("quote-after-line"); } static inline QString quoteBeforeLineAttribute() { return QStringLiteral("quote-before-line"); } static inline QString textAttribute() { return QStringLiteral("text"); } static inline QString nameAttribute() { return QStringLiteral("name"); } static inline QString sinceAttribute() { return QStringLiteral("since"); } +static inline QString defaultSuperclassAttribute() { return QStringLiteral("default-superclass"); } +static inline QString deleteInMainThreadAttribute() { return QStringLiteral("delete-in-main-thread"); } +static inline QString deprecatedAttribute() { return QStringLiteral("deprecated"); } +static inline QString exceptionHandlingAttribute() { return QStringLiteral("exception-handling"); } +static inline QString extensibleAttribute() { return QStringLiteral("extensible"); } static inline QString flagsAttribute() { return QStringLiteral("flags"); } +static inline QString forceAbstractAttribute() { return QStringLiteral("force-abstract"); } +static inline QString forceIntegerAttribute() { return QStringLiteral("force-integer"); } +static inline QString formatAttribute() { return QStringLiteral("format"); } static inline QString classAttribute() { return QStringLiteral("class"); } -static inline QString functionNameAttribute() { return QStringLiteral("function-name"); } -static inline QString fieldNameAttribute() { return QStringLiteral("field-name"); } -static inline QString enumNameAttribute() { return QStringLiteral("enum-name"); } -static inline QString argumentTypeAttribute() { return QStringLiteral("argument-type"); } -static inline QString returnTypeAttribute() { return QStringLiteral("return-type"); } +static inline QString generateAttribute() { return QStringLiteral("generate"); } +static inline QString genericClassAttribute() { return QStringLiteral("generic-class"); } +static inline QString indexAttribute() { return QStringLiteral("index"); } +static inline QString invalidateAfterUseAttribute() { return QStringLiteral("invalidate-after-use"); } +static inline QString locationAttribute() { return QStringLiteral("location"); } +static inline QString modifiedTypeAttribute() { return QStringLiteral("modified-type"); } +static inline QString modifierAttribute() { return QStringLiteral("modifier"); } +static inline QString ownershipAttribute() { return QStringLiteral("owner"); } +static inline QString packageAttribute() { return QStringLiteral("package"); } +static inline QString positionAttribute() { return QStringLiteral("position"); } +static inline QString preferredConversionAttribute() { return QStringLiteral("preferred-conversion"); } +static inline QString preferredTargetLangTypeAttribute() { return QStringLiteral("preferred-target-lang-type"); } +static inline QString removeAttribute() { return QStringLiteral("remove"); } +static inline QString renameAttribute() { return QStringLiteral("rename"); } +static inline QString readAttribute() { return QStringLiteral("read"); } +static inline QString writeAttribute() { return QStringLiteral("write"); } +static inline QString replaceAttribute() { return QStringLiteral("replace"); } +static inline QString toAttribute() { return QStringLiteral("to"); } +static inline QString signatureAttribute() { return QStringLiteral("signature"); } +static inline QString snippetAttribute() { return QStringLiteral("snippet"); } +static inline QString staticAttribute() { return QStringLiteral("static"); } +static inline QString threadAttribute() { return QStringLiteral("thread"); } +static inline QString sourceAttribute() { return QStringLiteral("source"); } +static inline QString streamAttribute() { return QStringLiteral("stream"); } static inline QString xPathAttribute() { return QStringLiteral("xpath"); } +static inline QString virtualSlotAttribute() { return QStringLiteral("virtual-slot"); } static inline QString enumIdentifiedByValueAttribute() { return QStringLiteral("identified-by-value"); } static inline QString noAttributeValue() { return QStringLiteral("no"); } @@ -90,105 +129,317 @@ static bool setRejectionRegularExpression(const QString &patternIn, return true; } -static bool addRejection(TypeDatabase *database, const QHash<QString, QString> &attributes, - QString *errorMessage) +// Extract a snippet from a file within annotation "// @snippet label". +static QString extractSnippet(const QString &code, const QString &snippetLabel) { - typedef QPair<QString, TypeRejection::MatchType> AttributeMatchTypePair; + if (snippetLabel.isEmpty()) + return code; + const QString pattern = QStringLiteral(R"(^\s*//\s*@snippet\s+)") + + QRegularExpression::escape(snippetLabel) + + QStringLiteral(R"(\s*$)"); + const QRegularExpression snippetRe(pattern); + Q_ASSERT(snippetRe.isValid()); - TypeRejection rejection; + bool useLine = false; + QString result; + const auto lines = code.splitRef(QLatin1Char('\n')); + for (const QStringRef &line : lines) { + if (snippetRe.match(line).hasMatch()) { + useLine = !useLine; + if (!useLine) + break; // End of snippet reached + } else if (useLine) + result += line.toString() + QLatin1Char('\n'); + } + return result; +} - const QString className = attributes.value(classAttribute()); - if (!setRejectionRegularExpression(className, &rejection.className, errorMessage)) - return false; +template <class EnumType, Qt::CaseSensitivity cs = Qt::CaseInsensitive> +struct EnumLookup +{ + QStringView name; + EnumType value; +}; + +template <class EnumType, Qt::CaseSensitivity cs> +bool operator==(const EnumLookup<EnumType, cs> &e1, const EnumLookup<EnumType, cs> &e2) +{ + return e1.name.compare(e2.name, cs) == 0; +} + +template <class EnumType, Qt::CaseSensitivity cs> +bool operator<(const EnumLookup<EnumType, cs> &e1, const EnumLookup<EnumType, cs> &e2) +{ + return e1.name.compare(e2.name, cs) < 0; +} + +// Helper macros to define lookup functions that take a QStringView needle +// and an optional default return value. +#define ENUM_LOOKUP_BEGIN(EnumType, caseSensitivity, functionName, defaultReturnValue) \ +static EnumType functionName(QStringView needle, EnumType defaultValue = defaultReturnValue) \ +{ \ + typedef EnumLookup<EnumType, caseSensitivity> HaystackEntry; \ + static const HaystackEntry haystack[] = + +#define ENUM_LOOKUP_LINEAR_SEARCH() \ + const auto end = haystack + sizeof(haystack) / sizeof(haystack[0]); \ + const auto it = std::find(haystack, end, HaystackEntry{needle, defaultValue}); \ + return it != end ? it->value : defaultValue; \ +} - static const AttributeMatchTypePair attributeMatchTypeMapping[] = - {{functionNameAttribute(), TypeRejection::Function}, - {fieldNameAttribute(), TypeRejection::Field}, - {enumNameAttribute(), TypeRejection::Enum}, - {argumentTypeAttribute(), TypeRejection::ArgumentType}, - {returnTypeAttribute(), TypeRejection::ReturnType} +#define ENUM_LOOKUP_BINARY_SEARCH() \ + const auto end = haystack + sizeof(haystack) / sizeof(haystack[0]); \ + const HaystackEntry needleEntry{needle, defaultValue}; \ + const auto lb = std::lower_bound(haystack, end, needleEntry); \ + return lb != end && *lb == needleEntry ? lb->value : defaultValue; \ +} + +ENUM_LOOKUP_BEGIN(TypeSystem::AllowThread, Qt::CaseInsensitive, + allowThreadFromAttribute, TypeSystem::AllowThread::Unspecified) + { + {QStringViewLiteral("yes"), TypeSystem::AllowThread::Allow}, + {QStringViewLiteral("true"), TypeSystem::AllowThread::Allow}, + {QStringViewLiteral("auto"), TypeSystem::AllowThread::Auto}, + {QStringViewLiteral("no"), TypeSystem::AllowThread::Disallow}, + {QStringViewLiteral("false"), TypeSystem::AllowThread::Disallow}, }; +ENUM_LOOKUP_LINEAR_SEARCH() - // Search for non-empty attribute (function, field, enum) - const auto aend = attributes.cend(); - for (const AttributeMatchTypePair &mapping : attributeMatchTypeMapping) { - const auto it = attributes.constFind(mapping.first); - if (it != aend && !it.value().isEmpty()) { - if (!setRejectionRegularExpression(it.value(), &rejection.pattern, errorMessage)) - return false; - rejection.matchType = mapping.second; - database->addRejection(rejection); - return true; - } - } +ENUM_LOOKUP_BEGIN(TypeSystem::Language, Qt::CaseInsensitive, + languageFromAttribute, TypeSystem::NoLanguage) + { + {QStringViewLiteral("all"), TypeSystem::All}, // sorted! + {QStringViewLiteral("constructors"), TypeSystem::Constructors}, + {QStringViewLiteral("destructor-function"), TypeSystem::DestructorFunction}, + {QStringViewLiteral("interface"), TypeSystem::Interface}, + {QStringViewLiteral("library-initializer"), TypeSystem::PackageInitializer}, + {QStringViewLiteral("native"), TypeSystem::NativeCode}, // em algum lugar do cpp + {QStringViewLiteral("shell"), TypeSystem::ShellCode}, // coloca no header, mas antes da declaracao da classe + {QStringViewLiteral("shell-declaration"), TypeSystem::ShellDeclaration}, + {QStringViewLiteral("target"), TypeSystem::TargetLangCode} // em algum lugar do cpp + }; +ENUM_LOOKUP_BINARY_SEARCH() - // Special case: When all fields except class are empty, completely exclude class - if (className == QLatin1String("*")) { - *errorMessage = QLatin1String("bad reject entry, neither 'class', 'function-name'" - " nor 'field' specified"); - return false; +ENUM_LOOKUP_BEGIN(TypeSystem::Ownership, Qt::CaseInsensitive, + ownershipFromFromAttribute, TypeSystem::InvalidOwnership) + { + {QStringViewLiteral("target"), TypeSystem::TargetLangOwnership}, + {QStringViewLiteral("c++"), TypeSystem::CppOwnership}, + {QStringViewLiteral("default"), TypeSystem::DefaultOwnership} + }; +ENUM_LOOKUP_LINEAR_SEARCH() + +ENUM_LOOKUP_BEGIN(AddedFunction::Access, Qt::CaseInsensitive, + addedFunctionAccessFromAttribute, AddedFunction::InvalidAccess) + { + {QStringViewLiteral("public"), AddedFunction::Public}, + {QStringViewLiteral("protected"), AddedFunction::Protected}, + }; +ENUM_LOOKUP_LINEAR_SEARCH() + +ENUM_LOOKUP_BEGIN(Modification::Modifiers, Qt::CaseSensitive, + modifierFromAttribute, Modification::InvalidModifier) + { + {QStringViewLiteral("private"), Modification::Private}, + {QStringViewLiteral("public"), Modification::Public}, + {QStringViewLiteral("protected"), Modification::Protected}, + {QStringViewLiteral("friendly"), Modification::Friendly}, + {QStringViewLiteral("rename"), Modification::Rename}, + {QStringViewLiteral("final"), Modification::Final}, + {QStringViewLiteral("non-final"), Modification::NonFinal} + }; +ENUM_LOOKUP_LINEAR_SEARCH() + +ENUM_LOOKUP_BEGIN(ReferenceCount::Action, Qt::CaseInsensitive, + referenceCountFromAttribute, ReferenceCount::Invalid) + { + {QStringViewLiteral("add"), ReferenceCount::Add}, + {QStringViewLiteral("add-all"), ReferenceCount::AddAll}, + {QStringViewLiteral("remove"), ReferenceCount::Remove}, + {QStringViewLiteral("set"), ReferenceCount::Set}, + {QStringViewLiteral("ignore"), ReferenceCount::Ignore} + }; +ENUM_LOOKUP_LINEAR_SEARCH() + +ENUM_LOOKUP_BEGIN(ArgumentOwner::Action, Qt::CaseInsensitive, + argumentOwnerActionFromAttribute, ArgumentOwner::Invalid) + { + {QStringViewLiteral("add"), ArgumentOwner::Add}, + {QStringViewLiteral("remove"), ArgumentOwner::Remove} + }; +ENUM_LOOKUP_LINEAR_SEARCH() + +ENUM_LOOKUP_BEGIN(TypeSystem::CodeSnipPosition, Qt::CaseInsensitive, + codeSnipPositionFromAttribute, TypeSystem::CodeSnipPositionInvalid) + { + {QStringViewLiteral("beginning"), TypeSystem::CodeSnipPositionBeginning}, + {QStringViewLiteral("end"), TypeSystem::CodeSnipPositionEnd}, + {QStringViewLiteral("declaration"), TypeSystem::CodeSnipPositionDeclaration}, + {QStringViewLiteral("prototype-initialization"), TypeSystem::CodeSnipPositionPrototypeInitialization}, + {QStringViewLiteral("constructor-initialization"), TypeSystem::CodeSnipPositionConstructorInitialization}, + {QStringViewLiteral("constructor"), TypeSystem::CodeSnipPositionConstructor} + }; +ENUM_LOOKUP_LINEAR_SEARCH() + +ENUM_LOOKUP_BEGIN(Include::IncludeType, Qt::CaseInsensitive, + locationFromAttribute, Include::InvalidInclude) + { + {QStringViewLiteral("global"), Include::IncludePath}, + {QStringViewLiteral("local"), Include::LocalPath}, + {QStringViewLiteral("target"), Include::TargetLangImport} + }; +ENUM_LOOKUP_LINEAR_SEARCH() + +ENUM_LOOKUP_BEGIN(TypeSystem::DocModificationMode, Qt::CaseInsensitive, + docModificationFromAttribute, TypeSystem::DocModificationInvalid) + { + {QStringViewLiteral("append"), TypeSystem::DocModificationAppend}, + {QStringViewLiteral("prepend"), TypeSystem::DocModificationPrepend}, + {QStringViewLiteral("replace"), TypeSystem::DocModificationReplace} + }; +ENUM_LOOKUP_LINEAR_SEARCH() + +ENUM_LOOKUP_BEGIN(ContainerTypeEntry::Type, Qt::CaseSensitive, + containerTypeFromAttribute, ContainerTypeEntry::NoContainer) + { + {QStringViewLiteral("list"), ContainerTypeEntry::ListContainer}, + {QStringViewLiteral("string-list"), ContainerTypeEntry::StringListContainer}, + {QStringViewLiteral("linked-list"), ContainerTypeEntry::LinkedListContainer}, + {QStringViewLiteral("vector"), ContainerTypeEntry::VectorContainer}, + {QStringViewLiteral("stack"), ContainerTypeEntry::StackContainer}, + {QStringViewLiteral("queue"), ContainerTypeEntry::QueueContainer}, + {QStringViewLiteral("set"), ContainerTypeEntry::SetContainer}, + {QStringViewLiteral("map"), ContainerTypeEntry::MapContainer}, + {QStringViewLiteral("multi-map"), ContainerTypeEntry::MultiMapContainer}, + {QStringViewLiteral("hash"), ContainerTypeEntry::HashContainer}, + {QStringViewLiteral("multi-hash"), ContainerTypeEntry::MultiHashContainer}, + {QStringViewLiteral("pair"), ContainerTypeEntry::PairContainer} + }; +ENUM_LOOKUP_LINEAR_SEARCH() + +ENUM_LOOKUP_BEGIN(TypeRejection::MatchType, Qt::CaseSensitive, + typeRejectionFromAttribute, TypeRejection::Invalid) + { + {QStringViewLiteral("class"), TypeRejection::ExcludeClass}, + {QStringViewLiteral("function-name"), TypeRejection::Function}, + {QStringViewLiteral("field-name"), TypeRejection::Field}, + {QStringViewLiteral("enum-name"), TypeRejection::Enum }, + {QStringViewLiteral("argument-type"), TypeRejection::ArgumentType}, + {QStringViewLiteral("return-type"), TypeRejection::ReturnType} + }; +ENUM_LOOKUP_LINEAR_SEARCH() + +ENUM_LOOKUP_BEGIN(TypeSystem::ExceptionHandling, Qt::CaseSensitive, + exceptionHandlingFromAttribute, TypeSystem::ExceptionHandling::Unspecified) +{ + {QStringViewLiteral("no"), TypeSystem::ExceptionHandling::Off}, + {QStringViewLiteral("false"), TypeSystem::ExceptionHandling::Off}, + {QStringViewLiteral("auto-off"), TypeSystem::ExceptionHandling::AutoDefaultToOff}, + {QStringViewLiteral("auto-on"), TypeSystem::ExceptionHandling::AutoDefaultToOn}, + {QStringViewLiteral("yes"), TypeSystem::ExceptionHandling::On}, + {QStringViewLiteral("true"), TypeSystem::ExceptionHandling::On}, +}; +ENUM_LOOKUP_LINEAR_SEARCH() + +ENUM_LOOKUP_BEGIN(StackElement::ElementType, Qt::CaseInsensitive, + elementFromTag, StackElement::None) + { + {QStringViewLiteral("access"), StackElement::Access}, // sorted! + {QStringViewLiteral("add-conversion"), StackElement::AddConversion}, + {QStringViewLiteral("add-function"), StackElement::AddFunction}, + {QStringViewLiteral("argument-map"), StackElement::ArgumentMap}, + {QStringViewLiteral("array"), StackElement::Array}, + {QStringViewLiteral("container-type"), StackElement::ContainerTypeEntry}, + {QStringViewLiteral("conversion-rule"), StackElement::ConversionRule}, + {QStringViewLiteral("custom-constructor"), StackElement::CustomMetaConstructor}, + {QStringViewLiteral("custom-destructor"), StackElement::CustomMetaDestructor}, + {QStringViewLiteral("custom-type"), StackElement::CustomTypeEntry}, + {QStringViewLiteral("define-ownership"), StackElement::DefineOwnership}, + {QStringViewLiteral("enum-type"), StackElement::EnumTypeEntry}, + {QStringViewLiteral("extra-includes"), StackElement::ExtraIncludes}, + {QStringViewLiteral("function"), StackElement::FunctionTypeEntry}, + {QStringViewLiteral("include"), StackElement::Include}, + {QStringViewLiteral("inject-code"), StackElement::InjectCode}, + {QStringViewLiteral("inject-documentation"), StackElement::InjectDocumentation}, + {QStringViewLiteral("insert-template"), StackElement::TemplateInstanceEnum}, + {QStringViewLiteral("interface-type"), StackElement::InterfaceTypeEntry}, + {QStringViewLiteral("load-typesystem"), StackElement::LoadTypesystem}, + {QStringViewLiteral("modify-argument"), StackElement::ModifyArgument}, + {QStringViewLiteral("modify-documentation"), StackElement::ModifyDocumentation}, + {QStringViewLiteral("modify-field"), StackElement::ModifyField}, + {QStringViewLiteral("modify-function"), StackElement::ModifyFunction}, + {QStringViewLiteral("namespace-type"), StackElement::NamespaceTypeEntry}, + {QStringViewLiteral("native-to-target"), StackElement::NativeToTarget}, + {QStringViewLiteral("no-null-pointer"), StackElement::NoNullPointers}, + {QStringViewLiteral("object-type"), StackElement::ObjectTypeEntry}, + {QStringViewLiteral("parent"), StackElement::ParentOwner}, + {QStringViewLiteral("primitive-type"), StackElement::PrimitiveTypeEntry}, + {QStringViewLiteral("reference-count"), StackElement::ReferenceCount}, + {QStringViewLiteral("reject-enum-value"), StackElement::RejectEnumValue}, + {QStringViewLiteral("rejection"), StackElement::Rejection}, + {QStringViewLiteral("remove"), StackElement::Removal}, + {QStringViewLiteral("remove-argument"), StackElement::RemoveArgument}, + {QStringViewLiteral("remove-default-expression"), StackElement::RemoveDefaultExpression}, + {QStringViewLiteral("rename"), StackElement::Rename}, + {QStringViewLiteral("replace"), StackElement::Replace}, + {QStringViewLiteral("replace-default-expression"), StackElement::ReplaceDefaultExpression}, + {QStringViewLiteral("replace-type"), StackElement::ReplaceType}, + {QStringViewLiteral("smart-pointer-type"), StackElement::SmartPointerTypeEntry}, + {QStringViewLiteral("suppress-warning"), StackElement::SuppressedWarning}, + {QStringViewLiteral("target-to-native"), StackElement::TargetToNative}, + {QStringViewLiteral("template"), StackElement::Template}, + {QStringViewLiteral("typedef-type"), StackElement::TypedefTypeEntry}, + {QStringViewLiteral("typesystem"), StackElement::Root}, + {QStringViewLiteral("value-type"), StackElement::ValueTypeEntry}, + }; +ENUM_LOOKUP_BINARY_SEARCH() + +static int indexOfAttribute(const QXmlStreamAttributes &atts, + QStringView name) +{ + for (int i = 0, size = atts.size(); i < size; ++i) { + if (atts.at(i).qualifiedName() == name) + return i; } - rejection.matchType = TypeRejection::ExcludeClass; - database->addRejection(rejection); - return true; + return -1; +} + +static QString msgMissingAttribute(const QString &a) +{ + return QLatin1String("Required attribute '") + a + + QLatin1String("' missing."); +} + +QTextStream &operator<<(QTextStream &str, const QXmlStreamAttribute &attribute) +{ + str << attribute.qualifiedName() << "=\"" << attribute.value() << '"'; + return str; +} + +static QString msgInvalidAttributeValue(const QXmlStreamAttribute &attribute) +{ + QString result; + QTextStream(&result) << "Invalid attribute value:" << attribute; + return result; } +static QString msgUnusedAttributes(const QStringRef &tag, const QXmlStreamAttributes &attributes) +{ + QString result; + QTextStream str(&result); + str << attributes.size() << " attributes(s) unused on <" << tag << ">: "; + for (int i = 0, size = attributes.size(); i < size; ++i) { + if (i) + str << ", "; + str << attributes.at(i); + } + return result; +} -Handler::Handler(TypeDatabase* database, bool generate) - : m_database(database), m_generate(generate ? TypeEntry::GenerateAll : TypeEntry::GenerateForSubclass) -{ - m_currentEnum = 0; - m_current = 0; - m_currentDroppedEntry = 0; - m_currentDroppedEntryDepth = 0; - m_ignoreDepth = 0; - - tagNames.insert(QLatin1String("rejection"), StackElement::Rejection); - tagNames.insert(QLatin1String("custom-type"), StackElement::CustomTypeEntry); - tagNames.insert(QLatin1String("primitive-type"), StackElement::PrimitiveTypeEntry); - tagNames.insert(QLatin1String("container-type"), StackElement::ContainerTypeEntry); - tagNames.insert(QLatin1String("object-type"), StackElement::ObjectTypeEntry); - tagNames.insert(QLatin1String("value-type"), StackElement::ValueTypeEntry); - tagNames.insert(QLatin1String("interface-type"), StackElement::InterfaceTypeEntry); - tagNames.insert(QLatin1String("namespace-type"), StackElement::NamespaceTypeEntry); - tagNames.insert(QLatin1String("enum-type"), StackElement::EnumTypeEntry); - tagNames.insert(QLatin1String("smart-pointer-type"), StackElement::SmartPointerTypeEntry); - tagNames.insert(QLatin1String("function"), StackElement::FunctionTypeEntry); - tagNames.insert(QLatin1String("extra-includes"), StackElement::ExtraIncludes); - tagNames.insert(QLatin1String("include"), StackElement::Include); - tagNames.insert(QLatin1String("inject-code"), StackElement::InjectCode); - tagNames.insert(QLatin1String("modify-function"), StackElement::ModifyFunction); - tagNames.insert(QLatin1String("modify-field"), StackElement::ModifyField); - tagNames.insert(QLatin1String("access"), StackElement::Access); - tagNames.insert(QLatin1String("remove"), StackElement::Removal); - tagNames.insert(QLatin1String("rename"), StackElement::Rename); - tagNames.insert(QLatin1String("typesystem"), StackElement::Root); - tagNames.insert(QLatin1String("custom-constructor"), StackElement::CustomMetaConstructor); - tagNames.insert(QLatin1String("custom-destructor"), StackElement::CustomMetaDestructor); - tagNames.insert(QLatin1String("argument-map"), StackElement::ArgumentMap); - tagNames.insert(QLatin1String("suppress-warning"), StackElement::SuppressedWarning); - tagNames.insert(QLatin1String("load-typesystem"), StackElement::LoadTypesystem); - tagNames.insert(QLatin1String("define-ownership"), StackElement::DefineOwnership); - tagNames.insert(QLatin1String("replace-default-expression"), StackElement::ReplaceDefaultExpression); - tagNames.insert(QLatin1String("reject-enum-value"), StackElement::RejectEnumValue); - tagNames.insert(QLatin1String("replace-type"), StackElement::ReplaceType); - tagNames.insert(QLatin1String("conversion-rule"), StackElement::ConversionRule); - tagNames.insert(QLatin1String("native-to-target"), StackElement::NativeToTarget); - tagNames.insert(QLatin1String("target-to-native"), StackElement::TargetToNative); - tagNames.insert(QLatin1String("add-conversion"), StackElement::AddConversion); - tagNames.insert(QLatin1String("modify-argument"), StackElement::ModifyArgument); - tagNames.insert(QLatin1String("remove-argument"), StackElement::RemoveArgument); - tagNames.insert(QLatin1String("remove-default-expression"), StackElement::RemoveDefaultExpression); - tagNames.insert(QLatin1String("template"), StackElement::Template); - tagNames.insert(QLatin1String("insert-template"), StackElement::TemplateInstanceEnum); - tagNames.insert(QLatin1String("replace"), StackElement::Replace); - 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); +Handler::Handler(TypeDatabase *database, bool generate) : + m_database(database), + m_generate(generate ? TypeEntry::GenerateAll : TypeEntry::GenerateForSubclass) +{ } static QString readerFileName(const QXmlStreamReader &reader) @@ -197,22 +448,72 @@ static QString readerFileName(const QXmlStreamReader &reader) return file != nullptr ? file->fileName() : QString(); } -static QString msgReaderError(const QXmlStreamReader &reader, const QString &what) +static QString msgReaderMessage(const QXmlStreamReader &reader, + const char *type, + const QString &what) { QString message; QTextStream str(&message); - str << "Error: "; + str << type << ": "; const QString fileName = readerFileName(reader); - if (!fileName.isEmpty()) - str << "file=" << QDir::toNativeSeparators(fileName) << ", "; - str << "line=" << reader.lineNumber() << ", column=" << reader.columnNumber() - << ", message=" << what; + if (fileName.isEmpty()) + str << "<stdin>:"; + else + str << QDir::toNativeSeparators(fileName) << ':'; + str << reader.lineNumber() << ':' << reader.columnNumber() + << ": " << what; return message; } -static QString msgReaderError(const QXmlStreamReader &reader) +static QString msgReaderWarning(const QXmlStreamReader &reader, const QString &what) { - return msgReaderError(reader, reader.errorString()); + return msgReaderMessage(reader, "Warning", what); +} + +static QString msgReaderError(const QXmlStreamReader &reader, const QString &what) +{ + return msgReaderMessage(reader, "Error", what); +} + +static QString msgUnimplementedElementWarning(const QXmlStreamReader &reader, + const QStringRef &name) +{ + const QString message = QLatin1String("The element \"") + + name + QLatin1String("\" is not implemented."); + return msgReaderMessage(reader, "Warning", message); +} + +static QString msgUnimplementedAttributeWarning(const QXmlStreamReader &reader, + const QStringRef &name) +{ + const QString message = QLatin1String("The attribute \"") + + name + QLatin1String("\" is not implemented."); + return msgReaderMessage(reader, "Warning", message); +} + +static inline QString msgUnimplementedAttributeWarning(const QXmlStreamReader &reader, + const QXmlStreamAttribute &attribute) +{ + return msgUnimplementedAttributeWarning(reader, attribute.qualifiedName()); +} + +static QString + msgUnimplementedAttributeValueWarning(const QXmlStreamReader &reader, + QStringView name, QStringView value) +{ + QString message; + QTextStream(&message) << "The value \"" << value + << "\" of the attribute \"" << name << "\" is not implemented."; + return msgReaderMessage(reader, "Warning", message); +} + +static inline + QString msgUnimplementedAttributeValueWarning(const QXmlStreamReader &reader, + const QXmlStreamAttribute &attribute) +{ + return msgUnimplementedAttributeValueWarning(reader, + attribute.qualifiedName(), + attribute.value()); } static QString msgInvalidVersion(const QStringRef &version, const QString &package = QString()) @@ -226,6 +527,53 @@ static QString msgInvalidVersion(const QStringRef &version, const QString &packa return result; } +static bool addRejection(TypeDatabase *database, QXmlStreamAttributes *attributes, + QString *errorMessage) +{ + const int classIndex = indexOfAttribute(*attributes, classAttribute()); + if (classIndex == -1) { + *errorMessage = msgMissingAttribute(classAttribute()); + return false; + } + + TypeRejection rejection; + const QString className = attributes->takeAt(classIndex).value().toString(); + if (!setRejectionRegularExpression(className, &rejection.className, errorMessage)) + return false; + + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + const TypeRejection::MatchType type = typeRejectionFromAttribute(name); + switch (type) { + case TypeRejection::Function: + case TypeRejection::Field: + case TypeRejection::Enum: + case TypeRejection::ArgumentType: + case TypeRejection::ReturnType: { + const QString pattern = attributes->takeAt(i).value().toString(); + if (!setRejectionRegularExpression(pattern, &rejection.pattern, errorMessage)) + return false; + rejection.matchType = type; + database->addRejection(rejection); + return true; + } + break; + default: + break; + } + } + + // Special case: When all fields except class are empty, completely exclude class + if (className == QLatin1String("*")) { + *errorMessage = QLatin1String("bad reject entry, neither 'class', 'function-name'" + " nor 'field' specified"); + return false; + } + rejection.matchType = TypeRejection::ExcludeClass; + database->addRejection(rejection); + return true; +} + bool Handler::parse(QXmlStreamReader &reader) { m_error.clear(); @@ -238,10 +586,10 @@ bool Handler::parse(QXmlStreamReader &reader) switch (reader.readNext()) { case QXmlStreamReader::NoToken: case QXmlStreamReader::Invalid: - qCWarning(lcShiboken).noquote().nospace() << msgReaderError(reader); + m_error = msgReaderError(reader, reader.errorString()); return false; case QXmlStreamReader::StartElement: - if (!startElement(reader.name(), reader.attributes())) { + if (!startElement(reader)) { m_error = msgReaderError(reader, m_error); return false; } @@ -271,22 +619,6 @@ bool Handler::parse(QXmlStreamReader &reader) return true; } -void Handler::fetchAttributeValues(const QString &name, const QXmlStreamAttributes &atts, - QHash<QString, QString> *acceptedAttributes) -{ - Q_ASSERT(acceptedAttributes); - - for (int i = 0; i < atts.length(); ++i) { - const QString key = atts.at(i).name().toString().toLower(); - if (!acceptedAttributes->contains(key)) { - qCWarning(lcShiboken).noquote().nospace() - << QStringLiteral("Unknown attribute for '%1': '%2'").arg(name, key); - } else { - acceptedAttributes->insert(key, atts.at(i).value().toString()); - } - } -} - bool Handler::endElement(const QStringRef &localName) { if (m_ignoreDepth) { @@ -416,14 +748,18 @@ bool Handler::endElement(const QStringRef &localName) break; } - if (m_current->type == StackElement::Root - || m_current->type == StackElement::NamespaceTypeEntry - || m_current->type == StackElement::InterfaceTypeEntry - || m_current->type == StackElement::ObjectTypeEntry - || m_current->type == StackElement::ValueTypeEntry - || m_current->type == StackElement::PrimitiveTypeEntry) { - StackElementContext* context = m_contextStack.pop(); - delete context; + switch (m_current->type) { + case StackElement::Root: + case StackElement::NamespaceTypeEntry: + case StackElement::InterfaceTypeEntry: + case StackElement::ObjectTypeEntry: + case StackElement::ValueTypeEntry: + case StackElement::PrimitiveTypeEntry: + case StackElement::TypedefTypeEntry: + delete m_contextStack.pop(); + break; + default: + break; } StackElement *child = m_current; @@ -551,8 +887,9 @@ bool Handler::importFileElement(const QXmlStreamAttributes &atts) return true; } -static bool convertBoolean(const QString &value, const QString &attributeName, bool defaultValue) +static bool convertBoolean(QStringView value, const QString &attributeName, bool defaultValue) { +#ifdef QTBUG_69389_FIXED if (value.compare(trueAttributeValue(), Qt::CaseInsensitive) == 0 || value.compare(yesAttributeValue(), Qt::CaseInsensitive) == 0) { return true; @@ -561,28 +898,47 @@ static bool convertBoolean(const QString &value, const QString &attributeName, b || value.compare(noAttributeValue(), Qt::CaseInsensitive) == 0) { return false; } +#else + if (QtPrivate::compareStrings(value, trueAttributeValue(), Qt::CaseInsensitive) == 0 + || QtPrivate::compareStrings(value, yesAttributeValue(), Qt::CaseInsensitive) == 0) { + return true; + } + if (QtPrivate::compareStrings(value, falseAttributeValue(), Qt::CaseInsensitive) == 0 + || QtPrivate::compareStrings(value, noAttributeValue(), Qt::CaseInsensitive) == 0) { + return false; + } +#endif const QString warn = QStringLiteral("Boolean value '%1' not supported in attribute '%2'. Use 'yes' or 'no'. Defaulting to '%3'.") - .arg(value, attributeName, + .arg(value) + .arg(attributeName, defaultValue ? yesAttributeValue() : noAttributeValue()); qCWarning(lcShiboken).noquote().nospace() << warn; return defaultValue; } -static bool convertRemovalAttribute(const QString& removalAttribute, Modification& mod, QString& errorMsg) +static bool convertRemovalAttribute(QStringView remove, Modification& mod, QString& errorMsg) { - QString remove = removalAttribute.toLower(); - if (!remove.isEmpty()) { - if (remove == QLatin1String("all")) { - mod.removal = TypeSystem::All; - } else if (remove == QLatin1String("target")) { - mod.removal = TypeSystem::TargetLangAndNativeCode; - } else { - errorMsg = QString::fromLatin1("Bad removal type '%1'").arg(remove); - return false; - } + if (remove.isEmpty()) + return true; +#ifdef QTBUG_69389_FIXED + if (remove.compare(QStringViewLiteral("all"), Qt::CaseInsensitive) == 0) { +#else + if (QtPrivate::compareStrings(remove, QStringViewLiteral("all"), Qt::CaseInsensitive) == 0) { +#endif + mod.removal = TypeSystem::All; + return true; } - return true; +#ifdef QTBUG_69389_FIXED + if (remove.compare(QStringViewLiteral("target"), Qt::CaseInsensitive) == 0) { +#else + if (QtPrivate::compareStrings(remove, QStringViewLiteral("target"), Qt::CaseInsensitive) == 0) { +#endif + mod.removal = TypeSystem::TargetLangAndNativeCode; + return true; + } + errorMsg = QString::fromLatin1("Bad removal type '%1'").arg(remove); + return false; } static void getNamePrefixRecursive(StackElement* element, QStringList& names) @@ -615,98 +971,1439 @@ static QString checkSignatureError(const QString& signature, const QString& tag) return QString(); } -void Handler::addFlags(const QString &name, QString flagName, - const QHash<QString, QString> &attributes, - const QVersionNumber &since) +void Handler::applyCommonAttributes(TypeEntry *type, QXmlStreamAttributes *attributes) const +{ + type->setCodeGeneration(m_generate); + const int revisionIndex = + indexOfAttribute(*attributes, QStringViewLiteral("revision")); + if (revisionIndex != -1) + type->setRevision(attributes->takeAt(revisionIndex).value().toInt()); +} + +FlagsTypeEntry * + Handler::parseFlagsEntry(const QXmlStreamReader &, + EnumTypeEntry *enumEntry, + const QString &name, QString flagName, + const QVersionNumber &since, + QXmlStreamAttributes *attributes) + { FlagsTypeEntry *ftype = new FlagsTypeEntry(QLatin1String("QFlags<") + name + QLatin1Char('>'), since); - ftype->setOriginator(m_currentEnum); + ftype->setOriginator(enumEntry); + ftype->setTargetLangPackage(enumEntry->targetLangPackage()); // Try to get the guess the qualified flag name const int lastSepPos = name.lastIndexOf(colonColon()); if (lastSepPos >= 0 && !flagName.contains(colonColon())) flagName.prepend(name.left(lastSepPos + 2)); ftype->setOriginalName(flagName); - ftype->setCodeGeneration(m_generate); + applyCommonAttributes(ftype, attributes); QString n = ftype->originalName(); QStringList lst = n.split(colonColon()); - if (QStringList(lst.mid(0, lst.size() - 1)).join(colonColon()) != m_currentEnum->targetLangQualifier()) { + const QString &targetLangQualifier = enumEntry->targetLangQualifier(); + if (QStringList(lst.mid(0, lst.size() - 1)).join(colonColon()) != targetLangQualifier) { qCWarning(lcShiboken).noquote().nospace() << QStringLiteral("enum %1 and flags %2 differ in qualifiers") - .arg(m_currentEnum->targetLangQualifier(), lst.constFirst()); + .arg(targetLangQualifier, lst.constFirst()); } ftype->setFlagsName(lst.constLast()); - m_currentEnum->setFlags(ftype); + enumEntry->setFlags(ftype); m_database->addFlagsType(ftype); m_database->addType(ftype); - QString revision = attributes.value(QLatin1String("flags-revision")); - if (revision.isEmpty()) - revision = attributes.value(QLatin1String("revision")); - setTypeRevision(ftype, revision.toInt()); -} + const int revisionIndex = + indexOfAttribute(*attributes, QStringViewLiteral("flags-revision")); + ftype->setRevision(revisionIndex != -1 + ? attributes->takeAt(revisionIndex).value().toInt() + : enumEntry->revision()); + return ftype; +} + +SmartPointerTypeEntry * + Handler::parseSmartPointerEntry(const QXmlStreamReader &, + const QString &name, const QVersionNumber &since, + QXmlStreamAttributes *attributes) +{ + QString smartPointerType; + QString getter; + QString refCountMethodName; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == QLatin1String("type")) { + smartPointerType = attributes->takeAt(i).value().toString(); + } else if (name == QLatin1String("getter")) { + getter = attributes->takeAt(i).value().toString(); + } else if (name == QLatin1String("ref-count-method")) { + refCountMethodName = attributes->takeAt(i).value().toString(); + } + } -bool Handler::handleSmartPointerEntry(StackElement *element, - QHash<QString, QString> &attributes, - const QString &name, - const QVersionNumber &since) -{ - QString smartPointerType = attributes[QLatin1String("type")]; if (smartPointerType.isEmpty()) { m_error = QLatin1String("No type specified for the smart pointer. Currently supported types: 'shared',"); - return false; + return nullptr; } if (smartPointerType != QLatin1String("shared")) { m_error = QLatin1String("Currently only the 'shared' type is supported."); - return false; + return nullptr; } - QString getter = attributes[QLatin1String("getter")]; if (getter.isEmpty()) { m_error = QLatin1String("No function getter name specified for getting the raw pointer held by the smart pointer."); - return false; + return nullptr; } - QString refCountMethodName = attributes[QLatin1String("ref-count-method")]; QString signature = getter + QLatin1String("()"); - signature = TypeDatabase::normalizedSignature(signature); if (signature.isEmpty()) { m_error = QLatin1String("No signature for the smart pointer getter found."); - return false; + return nullptr; } QString errorString = checkSignatureError(signature, QLatin1String("smart-pointer-type")); if (!errorString.isEmpty()) { m_error = errorString; - return false; + return nullptr; + } + + SmartPointerTypeEntry *type = + new SmartPointerTypeEntry(name, getter, smartPointerType, refCountMethodName, since); + applyCommonAttributes(type, attributes); + return type; +} + +PrimitiveTypeEntry * + Handler::parsePrimitiveTypeEntry(const QXmlStreamReader &reader, + const QString &name, const QVersionNumber &since, + QXmlStreamAttributes *attributes) +{ + PrimitiveTypeEntry *type = new PrimitiveTypeEntry(name, since); + applyCommonAttributes(type, attributes); + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == QLatin1String("target-lang-name")) { + type->setTargetLangName(attributes->takeAt(i).value().toString()); + } else if (name == QLatin1String("target-lang-api-name")) { + type->setTargetLangApiName(attributes->takeAt(i).value().toString()); + } else if (name == preferredConversionAttribute()) { + qCWarning(lcShiboken, "%s", + qPrintable(msgUnimplementedAttributeWarning(reader, name))); + } else if (name == preferredTargetLangTypeAttribute()) { + const bool v = convertBoolean(attributes->takeAt(i).value(), + preferredTargetLangTypeAttribute(), true); + type->setPreferredTargetLangType(v); + } else if (name == QLatin1String("default-constructor")) { + type->setDefaultConstructor(attributes->takeAt(i).value().toString()); + } } - SmartPointerTypeEntry *type = new SmartPointerTypeEntry(name, - getter, - smartPointerType, - refCountMethodName, - since); + if (type->targetLangName().isEmpty()) + type->setTargetLangName(type->name()); + if (type->targetLangApiName().isEmpty()) + type->setTargetLangApiName(type->name()); type->setTargetLangPackage(m_defaultPackage); - type->setCodeGeneration(m_generate); - element->entry = type; + return type; +} + +ContainerTypeEntry * + Handler::parseContainerTypeEntry(const QXmlStreamReader &, + const QString &name, const QVersionNumber &since, + QXmlStreamAttributes *attributes) +{ + const int typeIndex = indexOfAttribute(*attributes, QStringViewLiteral("type")); + if (typeIndex == -1) { + m_error = QLatin1String("no 'type' attribute specified"); + return nullptr; + } + const QStringRef typeName = attributes->takeAt(typeIndex).value(); + ContainerTypeEntry::Type containerType = containerTypeFromAttribute(typeName); + if (containerType == ContainerTypeEntry::NoContainer) { + m_error = QLatin1String("there is no container of type ") + typeName.toString(); + return nullptr; + } + ContainerTypeEntry *type = new ContainerTypeEntry(name, containerType, since); + applyCommonAttributes(type, attributes); + return type; +} + +EnumTypeEntry * + Handler::parseEnumTypeEntry(const QXmlStreamReader &reader, + const QString &fullName, const QVersionNumber &since, + QXmlStreamAttributes *attributes) +{ + QString scope; + QString name = fullName; + const int sep = fullName.lastIndexOf(colonColon()); + if (sep != -1) { + scope = fullName.left(sep); + name = fullName.right(fullName.size() - sep - 2); + } + EnumTypeEntry *entry = new EnumTypeEntry(scope, name, since); + applyCommonAttributes(entry, attributes); + entry->setTargetLangPackage(m_defaultPackage); + + QString flagNames; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == QLatin1String("upper-bound")) { + qCWarning(lcShiboken, "%s", + qPrintable(msgUnimplementedAttributeWarning(reader, name))); + } else if (name == QLatin1String("lower-bound")) { + qCWarning(lcShiboken, "%s", + qPrintable(msgUnimplementedAttributeWarning(reader, name))); + } else if (name == forceIntegerAttribute()) { + qCWarning(lcShiboken, "%s", + qPrintable(msgUnimplementedAttributeWarning(reader, name))); + } else if (name == extensibleAttribute()) { + qCWarning(lcShiboken, "%s", + qPrintable(msgUnimplementedAttributeWarning(reader, name))); + } else if (name == flagsAttribute()) { + flagNames = attributes->takeAt(i).value().toString(); + } + } + + // put in the flags parallel... + if (!flagNames.isEmpty()) { + const QStringList &flagNameList = flagNames.split(QLatin1Char(',')); + for (const QString &flagName : flagNameList) + parseFlagsEntry(reader, entry, fullName, flagName.trimmed(), since, attributes); + } + return entry; +} + +ObjectTypeEntry * + Handler::parseInterfaceTypeEntry(const QXmlStreamReader &, + const QString &name, const QVersionNumber &since, + QXmlStreamAttributes *attributes) +{ + ObjectTypeEntry *otype = new ObjectTypeEntry(name, since); + applyCommonAttributes(otype, attributes); + QString targetLangName = name; + bool generate = true; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == QLatin1String("target-lang-name")) { + targetLangName = attributes->takeAt(i).value().toString(); + } else if (name == generateAttribute()) { + generate = convertBoolean(attributes->takeAt(i).value(), + generateAttribute(), true); + } + } + + InterfaceTypeEntry *itype = + new InterfaceTypeEntry(InterfaceTypeEntry::interfaceName(targetLangName), since); + + if (generate) + itype->setCodeGeneration(m_generate); + else + itype->setCodeGeneration(TypeEntry::GenerateForSubclass); + + otype->setDesignatedInterface(itype); + itype->setOrigin(otype); + return otype; +} + +ValueTypeEntry * + Handler::parseValueTypeEntry(const QXmlStreamReader &, + const QString &name, const QVersionNumber &since, + QXmlStreamAttributes *attributes) +{ + ValueTypeEntry *typeEntry = new ValueTypeEntry(name, since); + applyCommonAttributes(typeEntry, attributes); + const int defaultCtIndex = + indexOfAttribute(*attributes, QStringViewLiteral("default-constructor")); + if (defaultCtIndex != -1) + typeEntry->setDefaultConstructor(attributes->takeAt(defaultCtIndex).value().toString()); + return typeEntry; +} + +FunctionTypeEntry * + Handler::parseFunctionTypeEntry(const QXmlStreamReader &, + const QString &name, const QVersionNumber &since, + QXmlStreamAttributes *attributes) +{ + const int signatureIndex = indexOfAttribute(*attributes, signatureAttribute()); + if (signatureIndex == -1) { + m_error = msgMissingAttribute(signatureAttribute()); + return nullptr; + } + const QString signature = + TypeDatabase::normalizedSignature(attributes->takeAt(signatureIndex).value().toString()); + + TypeEntry *existingType = m_database->findType(name); + + if (!existingType) { + FunctionTypeEntry *result = new FunctionTypeEntry(name, signature, since); + applyCommonAttributes(result, attributes); + return result; + } + + if (existingType->type() != TypeEntry::FunctionType) { + m_error = QStringLiteral("%1 expected to be a function, but isn't! Maybe it was already declared as a class or something else.") + .arg(name); + return nullptr; + } + + FunctionTypeEntry *result = reinterpret_cast<FunctionTypeEntry *>(existingType); + result->addSignature(signature); + return result; +} + +TypedefEntry * + Handler::parseTypedefEntry(const QXmlStreamReader &, const QString &name, + const QVersionNumber &since, + QXmlStreamAttributes *attributes) +{ + if (m_current && m_current->type != StackElement::Root + && m_current->type != StackElement::NamespaceTypeEntry) { + m_error = QLatin1String("typedef entries must be nested in namespaces or type system."); + return nullptr; + } + const int sourceIndex = indexOfAttribute(*attributes, sourceAttribute()); + if (sourceIndex == -1) { + m_error = msgMissingAttribute(sourceAttribute()); + return nullptr; + } + const QString sourceType = attributes->takeAt(sourceIndex).value().toString(); + auto result = new TypedefEntry(name, sourceType, since); + applyCommonAttributes(result, attributes); + return result; +} + +void Handler::applyComplexTypeAttributes(const QXmlStreamReader &reader, + ComplexTypeEntry *ctype, + QXmlStreamAttributes *attributes) const +{ + bool generate = true; + ctype->setCopyable(ComplexTypeEntry::Unknown); + auto exceptionHandling = m_exceptionHandling; + + QString package = m_defaultPackage; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == streamAttribute()) { + ctype->setStream(convertBoolean(attributes->takeAt(i).value(), streamAttribute(), false)); + } else if (name == generateAttribute()) { + generate = convertBoolean(attributes->takeAt(i).value(), generateAttribute(), true); + } else if (name ==packageAttribute()) { + package = attributes->takeAt(i).value().toString(); + } else if (name == defaultSuperclassAttribute()) { + ctype->setDefaultSuperclass(attributes->takeAt(i).value().toString()); + } else if (name == genericClassAttribute()) { + qCWarning(lcShiboken, "%s", + qPrintable(msgUnimplementedAttributeWarning(reader, name))); + const bool v = convertBoolean(attributes->takeAt(i).value(), genericClassAttribute(), false); + ctype->setGenericClass(v); + } else if (name == QLatin1String("target-lang-name")) { + ctype->setTargetLangName(attributes->takeAt(i).value().toString()); + } else if (name == QLatin1String("polymorphic-base")) { + ctype->setPolymorphicIdValue(attributes->takeAt(i).value().toString()); + } else if (name == QLatin1String("polymorphic-id-expression")) { + ctype->setPolymorphicIdValue(attributes->takeAt(i).value().toString()); + } else if (name == copyableAttribute()) { + const bool v = convertBoolean(attributes->takeAt(i).value(), copyableAttribute(), false); + ctype->setCopyable(v ? ComplexTypeEntry::CopyableSet : ComplexTypeEntry::NonCopyableSet); + } else if (name == exceptionHandlingAttribute()) { + const auto attribute = attributes->takeAt(i); + const auto v = exceptionHandlingFromAttribute(attribute.value()); + if (v != TypeSystem::ExceptionHandling::Unspecified) { + exceptionHandling = v; + } else { + qCWarning(lcShiboken, "%s", + qPrintable(msgInvalidAttributeValue(attribute))); + } + } else if (name == QLatin1String("held-type")) { + qCWarning(lcShiboken, "%s", + qPrintable(msgUnimplementedAttributeWarning(reader, name))); + } else if (name == QLatin1String("hash-function")) { + ctype->setHashFunction(attributes->takeAt(i).value().toString()); + } else if (name == forceAbstractAttribute()) { + qCWarning(lcShiboken, "%s", + qPrintable(msgUnimplementedAttributeWarning(reader, name))); + } else if (name == deprecatedAttribute()) { + if (convertBoolean(attributes->takeAt(i).value(), deprecatedAttribute(), false)) + ctype->setTypeFlags(ctype->typeFlags() | ComplexTypeEntry::Deprecated); + } else if (name == deleteInMainThreadAttribute()) { + if (convertBoolean(attributes->takeAt(i).value(), deleteInMainThreadAttribute(), false)) + ctype->setDeleteInMainThread(true); + } else if (name == QLatin1String("target-type")) { + ctype->setTargetType(attributes->takeAt(i).value().toString()); + } + } + + if (exceptionHandling != TypeSystem::ExceptionHandling::Unspecified) + ctype->setExceptionHandling(exceptionHandling); + + // The generator code relies on container's package being empty. + if (ctype->type() != TypeEntry::ContainerType) + ctype->setTargetLangPackage(package); + + if (InterfaceTypeEntry *di = ctype->designatedInterface()) + di->setTargetLangPackage(package); + + if (generate) + ctype->setCodeGeneration(m_generate); + else + ctype->setCodeGeneration(TypeEntry::GenerateForSubclass); +} + +bool Handler::parseRenameFunction(const QXmlStreamReader &, + QString *name, QXmlStreamAttributes *attributes) +{ + QString signature; + QString rename; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == signatureAttribute()) { + // Do not remove as it is needed for the type entry later on + signature = attributes->at(i).value().toString(); + } else if (name == renameAttribute()) { + rename = attributes->takeAt(i).value().toString(); + } + } + + if (signature.isEmpty()) { + m_error = msgMissingAttribute(signatureAttribute()); + return false; + } + + *name = signature.left(signature.indexOf(QLatin1Char('('))).trimmed(); + + QString errorString = checkSignatureError(signature, QLatin1String("function")); + if (!errorString.isEmpty()) { + m_error = errorString; + return false; + } + + if (!rename.isEmpty()) { + static const QRegularExpression functionNameRegExp(QLatin1String("^[a-zA-Z_][a-zA-Z0-9_]*$")); + Q_ASSERT(functionNameRegExp.isValid()); + if (!functionNameRegExp.match(rename).hasMatch()) { + m_error = QLatin1String("can not rename '") + signature + QLatin1String("', '") + + rename + QLatin1String("' is not a valid function name"); + return false; + } + FunctionModification mod; + if (!mod.setSignature(signature, &m_error)) + return false; + mod.renamedToName = rename; + mod.modifiers |= Modification::Rename; + m_contextStack.top()->functionMods << mod; + } + return true; +} + +bool Handler::parseInjectDocumentation(const QXmlStreamReader &, + QXmlStreamAttributes *attributes) +{ + const int validParent = StackElement::TypeEntryMask + | StackElement::ModifyFunction + | StackElement::ModifyField; + if (!m_current->parent || (m_current->parent->type & validParent) == 0) { + m_error = QLatin1String("inject-documentation must be inside modify-function, " + "modify-field or other tags that creates a type"); + return false; + } + + TypeSystem::DocModificationMode mode = TypeSystem::DocModificationReplace; + TypeSystem::Language lang = TypeSystem::NativeCode; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == QLatin1String("mode")) { + const QStringRef modeName = attributes->takeAt(i).value(); + mode = docModificationFromAttribute(modeName); + if (mode == TypeSystem::DocModificationInvalid) { + m_error = QLatin1String("Unknown documentation injection mode: ") + modeName; + return false; + } + } else if (name == formatAttribute()) { + const QStringRef format = attributes->takeAt(i).value(); + lang = languageFromAttribute(format); + if (lang != TypeSystem::TargetLangCode && lang != TypeSystem::NativeCode) { + m_error = QStringLiteral("unsupported class attribute: '%1'").arg(format); + return false; + } + } + } + + QString signature = m_current->type & StackElement::TypeEntryMask + ? QString() : m_currentSignature; + DocModification mod(mode, signature); + mod.setFormat(lang); + m_contextStack.top()->docModifications << mod; + return true; +} + +bool Handler::parseModifyDocumentation(const QXmlStreamReader &, + QXmlStreamAttributes *attributes) +{ + const int validParent = StackElement::TypeEntryMask + | StackElement::ModifyFunction + | StackElement::ModifyField; + if (!m_current->parent || (m_current->parent->type & validParent) == 0) { + m_error = QLatin1String("modify-documentation must be inside modify-function, " + "modify-field or other tags that creates a type"); + return false; + } + + const int xpathIndex = indexOfAttribute(*attributes, xPathAttribute()); + if (xpathIndex == -1) { + m_error = msgMissingAttribute(xPathAttribute()); + return false; + } + + const QString xpath = attributes->takeAt(xpathIndex).value().toString(); + QString signature = (m_current->type & StackElement::TypeEntryMask) ? QString() : m_currentSignature; + m_contextStack.top()->docModifications + << DocModification(xpath, signature); + return true; +} + +// m_exceptionHandling +TypeSystemTypeEntry *Handler::parseRootElement(const QXmlStreamReader &, + const QVersionNumber &since, + QXmlStreamAttributes *attributes) +{ + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == packageAttribute()) { + m_defaultPackage = attributes->takeAt(i).value().toString(); + } else if (name == defaultSuperclassAttribute()) { + m_defaultSuperclass = attributes->takeAt(i).value().toString(); + } else if (name == exceptionHandlingAttribute()) { + const auto attribute = attributes->takeAt(i); + const auto v = exceptionHandlingFromAttribute(attribute.value()); + if (v != TypeSystem::ExceptionHandling::Unspecified) { + m_exceptionHandling = v; + } else { + qCWarning(lcShiboken, "%s", + qPrintable(msgInvalidAttributeValue(attribute))); + } + } + } + + TypeSystemTypeEntry *moduleEntry = + const_cast<TypeSystemTypeEntry *>(m_database->findTypeSystemType(m_defaultPackage)); + if (!moduleEntry) + moduleEntry = new TypeSystemTypeEntry(m_defaultPackage, since); + moduleEntry->setCodeGeneration(m_generate); + + if ((m_generate == TypeEntry::GenerateForSubclass || + m_generate == TypeEntry::GenerateNothing) && !m_defaultPackage.isEmpty()) + TypeDatabase::instance()->addRequiredTargetImport(m_defaultPackage); + + if (!moduleEntry->qualifiedCppName().isEmpty()) + m_database->addType(moduleEntry); + return moduleEntry; +} + +bool Handler::loadTypesystem(const QXmlStreamReader &, + QXmlStreamAttributes *attributes) +{ + QString typeSystemName; + bool generateChild = true; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == nameAttribute()) + typeSystemName = attributes->takeAt(i).value().toString(); + else if (name == generateAttribute()) + generateChild = convertBoolean(attributes->takeAt(i).value(), generateAttribute(), true); + } + if (typeSystemName.isEmpty()) { + m_error = QLatin1String("No typesystem name specified"); + return false; + } + const bool result = + m_database->parseFile(typeSystemName, m_currentPath, generateChild + && m_generate == TypeEntry::GenerateAll); + if (!result) + m_error = QStringLiteral("Failed to parse: '%1'").arg(typeSystemName); + return result; +} + +bool Handler::parseRejectEnumValue(const QXmlStreamReader &, + QXmlStreamAttributes *attributes) +{ + if (!m_currentEnum) { + m_error = QLatin1String("<reject-enum-value> node must be used inside a <enum-type> node"); + return false; + } + const int nameIndex = indexOfAttribute(*attributes, nameAttribute()); + if (nameIndex == -1) { + m_error = msgMissingAttribute(nameAttribute()); + return false; + } + m_currentEnum->addEnumValueRejection(attributes->takeAt(nameIndex).value().toString()); + return true; +} + +bool Handler::parseReplaceArgumentType(const QXmlStreamReader &, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + if (topElement.type != StackElement::ModifyArgument) { + m_error = QLatin1String("Type replacement can only be specified for argument modifications"); + return false; + } + const int modifiedTypeIndex = indexOfAttribute(*attributes, modifiedTypeAttribute()); + if (modifiedTypeIndex == -1) { + m_error = QLatin1String("Type replacement requires 'modified-type' attribute"); + return false; + } + m_contextStack.top()->functionMods.last().argument_mods.last().modified_type = + attributes->takeAt(modifiedTypeIndex).value().toString(); + return true; +} + +bool Handler::parseCustomConversion(const QXmlStreamReader &, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + if (topElement.type != StackElement::ModifyArgument + && topElement.type != StackElement::ValueTypeEntry + && topElement.type != StackElement::PrimitiveTypeEntry + && topElement.type != StackElement::ContainerTypeEntry) { + m_error = QLatin1String("Conversion rules can only be specified for argument modification, " + "value-type, primitive-type or container-type conversion."); + return false; + } + + QString sourceFile; + QString snippetLabel; + TypeSystem::Language lang = TypeSystem::NativeCode; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == classAttribute()) { + const QStringRef languageAttribute = attributes->takeAt(i).value(); + lang = languageFromAttribute(languageAttribute); + if (lang != TypeSystem::TargetLangCode && lang != TypeSystem::NativeCode) { + m_error = QStringLiteral("unsupported class attribute: '%1'").arg(languageAttribute); + return false; + } + } else if (name == QLatin1String("file")) { + sourceFile = attributes->takeAt(i).value().toString(); + } else if (name == snippetAttribute()) { + snippetLabel = attributes->takeAt(i).value().toString(); + } + } + + if (topElement.type == StackElement::ModifyArgument) { + CodeSnip snip; + snip.language = lang; + m_contextStack.top()->functionMods.last().argument_mods.last().conversion_rules.append(snip); + return true; + } + + if (topElement.entry->hasConversionRule() || topElement.entry->hasCustomConversion()) { + m_error = QLatin1String("Types can have only one conversion rule"); + return false; + } + + // The old conversion rule tag that uses a file containing the conversion + // will be kept temporarily for compatibility reasons. + if (!sourceFile.isEmpty()) { + if (m_generate != TypeEntry::GenerateForSubclass + && m_generate != TypeEntry::GenerateNothing) { + + const char* conversionFlag = NATIVE_CONVERSION_RULE_FLAG; + if (lang == TypeSystem::TargetLangCode) + conversionFlag = TARGET_CONVERSION_RULE_FLAG; + + QFile conversionSource(sourceFile); + if (conversionSource.open(QIODevice::ReadOnly | QIODevice::Text)) { + const QString conversionRule = + extractSnippet(QString::fromUtf8(conversionSource.readAll()), snippetLabel); + topElement.entry->setConversionRule(QLatin1String(conversionFlag) + conversionRule); + } else { + qCWarning(lcShiboken).noquote().nospace() + << "File containing conversion code for " + << topElement.entry->name() << " type does not exist or is not readable: " + << sourceFile; + } + } + } + + CustomConversion* customConversion = new CustomConversion(m_current->entry); + customConversionsForReview.append(customConversion); + return true; +} + +bool Handler::parseAddConversion(const QXmlStreamReader &, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + if (topElement.type != StackElement::TargetToNative) { + m_error = QLatin1String("Target to Native conversions can only be added inside 'target-to-native' tags."); + return false; + } + QString sourceTypeName; + QString typeCheck; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == QLatin1String("type")) + sourceTypeName = attributes->takeAt(i).value().toString(); + else if (name == QLatin1String("check")) + typeCheck = attributes->takeAt(i).value().toString(); + } + if (sourceTypeName.isEmpty()) { + m_error = QLatin1String("Target to Native conversions must specify the input type with the 'type' attribute."); + return false; + } + m_current->entry->customConversion()->addTargetToNativeConversion(sourceTypeName, typeCheck); + m_contextStack.top()->codeSnips << CodeSnip(); + return true; +} + +static bool parseIndex(const QString &index, int *result, QString *errorMessage) +{ + bool ok = false; + *result = index.toInt(&ok); + if (!ok) + *errorMessage = QStringLiteral("Cannot convert '%1' to integer").arg(index); + return ok; +} + +static bool parseArgumentIndex(const QString &index, int *result, QString *errorMessage) +{ + if (index == QLatin1String("return")) { + *result = 0; + return true; + } + if (index == QLatin1String("this")) { + *result = -1; + return true; + } + return parseIndex(index, result, errorMessage); +} + +bool Handler::parseModifyArgument(const QXmlStreamReader &, + const StackElement &topElement, QXmlStreamAttributes *attributes) +{ + if (topElement.type != StackElement::ModifyFunction + && topElement.type != StackElement::AddFunction) { + m_error = QString::fromLatin1("argument modification requires function" + " modification as parent, was %1") + .arg(topElement.type, 0, 16); + return false; + } + + QString index; + QString replaceValue; + bool resetAfterUse = false; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == indexAttribute()) { + index = attributes->takeAt(i).value().toString(); + } else if (name == QLatin1String("replace-value")) { + replaceValue = attributes->takeAt(i).value().toString(); + } else if (name == invalidateAfterUseAttribute()) { + resetAfterUse = convertBoolean(attributes->takeAt(i).value(), + invalidateAfterUseAttribute(), false); + } + } + + if (index.isEmpty()) { + m_error = msgMissingAttribute(indexAttribute()); + return false; + } + + int idx; + if (!parseArgumentIndex(index, &idx, &m_error)) + return false; + + if (!replaceValue.isEmpty() && idx) { + m_error = QLatin1String("replace-value is only supported for return values (index=0)."); + return false; + } + + ArgumentModification argumentModification = ArgumentModification(idx); + argumentModification.replace_value = replaceValue; + argumentModification.resetAfterUse = resetAfterUse; + m_contextStack.top()->functionMods.last().argument_mods.append(argumentModification); + return true; +} + +bool Handler::parseNoNullPointer(const QXmlStreamReader &reader, + const StackElement &topElement, QXmlStreamAttributes *attributes) +{ + if (topElement.type != StackElement::ModifyArgument) { + m_error = QLatin1String("no-null-pointer requires argument modification as parent"); + return false; + } + + ArgumentModification &lastArgMod = m_contextStack.top()->functionMods.last().argument_mods.last(); + lastArgMod.noNullPointers = true; + + const int defaultValueIndex = + indexOfAttribute(*attributes, QStringViewLiteral("default-value")); + if (defaultValueIndex != -1) { + const QXmlStreamAttribute attribute = attributes->takeAt(defaultValueIndex); + qCWarning(lcShiboken, "%s", + qPrintable(msgUnimplementedAttributeWarning(reader, attribute))); + } + return true; +} + +bool Handler::parseDefineOwnership(const QXmlStreamReader &, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + if (topElement.type != StackElement::ModifyArgument) { + m_error = QLatin1String("define-ownership requires argument modification as parent"); + return false; + } + + TypeSystem::Language lang = TypeSystem::TargetLangCode; + QString ownership; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == classAttribute()) { + const QStringRef className = attributes->takeAt(i).value(); + lang = languageFromAttribute(className); + if (lang != TypeSystem::TargetLangCode && lang != TypeSystem::NativeCode) { + m_error = QStringLiteral("unsupported class attribute: '%1'").arg(className); + return false; + } + } else if (name == ownershipAttribute()) { + ownership = attributes->takeAt(i).value().toString(); + } + } + const TypeSystem::Ownership owner = ownershipFromFromAttribute(ownership); + if (owner == TypeSystem::InvalidOwnership) { + m_error = QStringLiteral("unsupported owner attribute: '%1'").arg(ownership); + return false; + } + m_contextStack.top()->functionMods.last().argument_mods.last().ownerships[lang] = owner; return true; } -bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts) +bool Handler::parseArgumentMap(const QXmlStreamReader &, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + if (!(topElement.type & StackElement::CodeSnipMask)) { + m_error = QLatin1String("Argument maps requires code injection as parent"); + return false; + } + + int pos = 1; + QString metaName; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == indexAttribute()) { + if (!parseIndex(attributes->takeAt(i).value().toString(), &pos, &m_error)) + return false; + if (pos <= 0) { + m_error = QStringLiteral("Argument position %1 must be a positive number").arg(pos); + return false; + } + } else if (name == QLatin1String("meta-name")) { + metaName = attributes->takeAt(i).value().toString(); + } + } + + if (metaName.isEmpty()) + qCWarning(lcShiboken) << "Empty meta name in argument map"; + + if (topElement.type == StackElement::InjectCodeInFunction) { + m_contextStack.top()->functionMods.last().snips.last().argumentMap[pos] = metaName; + } else { + qCWarning(lcShiboken) << "Argument maps are only useful for injection of code " + "into functions."; + } + return true; +} + +bool Handler::parseRemoval(const QXmlStreamReader &, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + if (topElement.type != StackElement::ModifyFunction) { + m_error = QLatin1String("Function modification parent required"); + return false; + } + + TypeSystem::Language lang = TypeSystem::All; + const int classIndex = indexOfAttribute(*attributes, classAttribute()); + if (classIndex != -1) { + const QStringRef value = attributes->takeAt(classIndex).value(); + lang = languageFromAttribute(value); + if (lang == TypeSystem::TargetLangCode) // "target" means TargetLangAndNativeCode here + lang = TypeSystem::TargetLangAndNativeCode; + if (lang != TypeSystem::TargetLangAndNativeCode && lang != TypeSystem::All) { + m_error = QStringLiteral("unsupported class attribute: '%1'").arg(value); + return false; + } + } + m_contextStack.top()->functionMods.last().removal = lang; + return true; +} + +bool Handler::parseRename(const QXmlStreamReader &reader, + StackElement::ElementType type, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + if (topElement.type != StackElement::ModifyField + && topElement.type != StackElement::ModifyFunction + && topElement.type != StackElement::ModifyArgument) { + m_error = QLatin1String("Function, field or argument modification parent required"); + return false; + } + + Modification *mod = nullptr; + if (topElement.type == StackElement::ModifyFunction) + mod = &m_contextStack.top()->functionMods.last(); + else if (topElement.type == StackElement::ModifyField) + mod = &m_contextStack.top()->fieldMods.last(); + + Modification::Modifiers modifierFlag = Modification::Rename; + if (type == StackElement::Rename) { + const int toIndex = indexOfAttribute(*attributes, toAttribute()); + if (toIndex == -1) { + m_error = msgMissingAttribute(toAttribute()); + return false; + } + const QString renamed_to = attributes->takeAt(toIndex).value().toString(); + if (topElement.type == StackElement::ModifyFunction) + mod->setRenamedTo(renamed_to); + else if (topElement.type == StackElement::ModifyField) + mod->setRenamedTo(renamed_to); + else + m_contextStack.top()->functionMods.last().argument_mods.last().renamed_to = renamed_to; + } else { + const int modifierIndex = indexOfAttribute(*attributes, modifierAttribute()); + if (modifierIndex == -1) { + m_error = msgMissingAttribute(modifierAttribute()); + return false; + } + const QStringRef modifier = attributes->takeAt(modifierIndex).value(); + modifierFlag = modifierFromAttribute(modifier); + if (modifierFlag == Modification::InvalidModifier) { + m_error = QStringLiteral("Unknown access modifier: '%1'").arg(modifier); + return false; + } + if (modifierFlag == Modification::Friendly) { + qCWarning(lcShiboken, "%s", + qPrintable(msgUnimplementedAttributeValueWarning(reader, modifierAttribute(), modifier))); + } + } + + if (mod) + mod->modifiers |= modifierFlag; + return true; +} + +bool Handler::parseModifyField(const QXmlStreamReader &reader, + QXmlStreamAttributes *attributes) +{ + FieldModification fm; + fm.modifiers = FieldModification::Readable | FieldModification::Writable; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == nameAttribute()) { + fm.name = attributes->takeAt(i).value().toString(); + } else if (name == removeAttribute()) { + if (!convertRemovalAttribute(attributes->takeAt(i).value(), fm, m_error)) + return false; + } else if (name == readAttribute()) { + qCWarning(lcShiboken, "%s", + qPrintable(msgUnimplementedAttributeWarning(reader, name))); + if (!convertBoolean(attributes->takeAt(i).value(), readAttribute(), true)) + fm.modifiers &= ~FieldModification::Readable; + } else if (name == writeAttribute()) { + qCWarning(lcShiboken, "%s", + qPrintable(msgUnimplementedAttributeWarning(reader, name))); + if (!convertBoolean(attributes->takeAt(i).value(), writeAttribute(), true)) + fm.modifiers &= ~FieldModification::Writable; + } + } + if (fm.name.isEmpty()) { + m_error = msgMissingAttribute(nameAttribute()); + return false; + } + m_contextStack.top()->fieldMods << fm; + return true; +} + +bool Handler::parseAddFunction(const QXmlStreamReader &, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + if (!(topElement.type & (StackElement::ComplexTypeEntryMask | StackElement::Root))) { + m_error = QString::fromLatin1("Add function requires a complex type or a root tag as parent" + ", was=%1").arg(topElement.type, 0, 16); + return false; + } + QString originalSignature; + QString returnType = QLatin1String("void"); + bool staticFunction = false; + QString access; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == QLatin1String("signature")) { + originalSignature = attributes->takeAt(i).value().toString(); + } else if (name == QLatin1String("return-type")) { + returnType = attributes->takeAt(i).value().toString(); + } else if (name == staticAttribute()) { + staticFunction = convertBoolean(attributes->takeAt(i).value(), + staticAttribute(), false); + } else if (name == accessAttribute()) { + access = attributes->takeAt(i).value().toString(); + } + } + + QString signature = TypeDatabase::normalizedSignature(originalSignature); + if (signature.isEmpty()) { + m_error = QLatin1String("No signature for the added function"); + return false; + } + + QString errorString = checkSignatureError(signature, QLatin1String("add-function")); + if (!errorString.isEmpty()) { + m_error = errorString; + return false; + } + + AddedFunction func(signature, returnType); + func.setStatic(staticFunction); + if (!signature.contains(QLatin1Char('('))) + signature += QLatin1String("()"); + m_currentSignature = signature; + + if (!access.isEmpty()) { + const AddedFunction::Access a = addedFunctionAccessFromAttribute(access); + if (a == AddedFunction::InvalidAccess) { + m_error = QString::fromLatin1("Bad access type '%1'").arg(access); + return false; + } + func.setAccess(a); + } + + m_contextStack.top()->addedFunctions << func; + + FunctionModification mod; + if (!mod.setSignature(m_currentSignature, &m_error)) + return false; + mod.setOriginalSignature(originalSignature); + m_contextStack.top()->functionMods << mod; + return true; +} + +bool Handler::parseModifyFunction(const QXmlStreamReader &reader, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + if (!(topElement.type & StackElement::ComplexTypeEntryMask)) { + m_error = QString::fromLatin1("Modify function requires complex type as parent" + ", was=%1").arg(topElement.type, 0, 16); + return false; + } + + QString originalSignature; + QString access; + QString removal; + QString rename; + QString association; + bool deprecated = false; + bool isThread = false; + TypeSystem::ExceptionHandling exceptionHandling = TypeSystem::ExceptionHandling::Unspecified; + TypeSystem::AllowThread allowThread = TypeSystem::AllowThread::Unspecified; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == QLatin1String("signature")) { + originalSignature = attributes->takeAt(i).value().toString(); + } else if (name == accessAttribute()) { + access = attributes->takeAt(i).value().toString(); + } else if (name == renameAttribute()) { + rename = attributes->takeAt(i).value().toString(); + } else if (name == QLatin1String("associated-to")) { + association = attributes->takeAt(i).value().toString(); + qCWarning(lcShiboken, "%s", + qPrintable(msgUnimplementedAttributeWarning(reader, name))); + } else if (name == removeAttribute()) { + removal = attributes->takeAt(i).value().toString(); + } else if (name == deprecatedAttribute()) { + deprecated = convertBoolean(attributes->takeAt(i).value(), + deprecatedAttribute(), false); + } else if (name == threadAttribute()) { + isThread = convertBoolean(attributes->takeAt(i).value(), + threadAttribute(), false); + } else if (name == allowThreadAttribute()) { + const QXmlStreamAttribute attribute = attributes->takeAt(i); + allowThread = allowThreadFromAttribute(attribute.value()); + if (allowThread == TypeSystem::AllowThread::Unspecified) { + m_error = msgInvalidAttributeValue(attribute); + return false; + } + } else if (name == exceptionHandlingAttribute()) { + const auto attribute = attributes->takeAt(i); + exceptionHandling = exceptionHandlingFromAttribute(attribute.value()); + if (exceptionHandling == TypeSystem::ExceptionHandling::Unspecified) { + qCWarning(lcShiboken, "%s", + qPrintable(msgInvalidAttributeValue(attribute))); + } + } else if (name == virtualSlotAttribute()) { + qCWarning(lcShiboken, "%s", + qPrintable(msgUnimplementedAttributeWarning(reader, name))); + } + } + + const QString signature = TypeDatabase::normalizedSignature(originalSignature); + if (signature.isEmpty()) { + m_error = QLatin1String("No signature for modified function"); + return false; + } + + QString errorString = checkSignatureError(signature, QLatin1String("modify-function")); + if (!errorString.isEmpty()) { + m_error = errorString; + return false; + } + + FunctionModification mod; + if (!mod.setSignature(signature, &m_error)) + return false; + mod.setOriginalSignature(originalSignature); + mod.setExceptionHandling(exceptionHandling); + m_currentSignature = signature; + + if (!access.isEmpty()) { + const Modification::Modifiers m = modifierFromAttribute(access); + if ((m & (Modification::AccessModifierMask | Modification::FinalMask)) == 0) { + m_error = QString::fromLatin1("Bad access type '%1'").arg(access); + return false; + } + if (m == Modification::Final || m == Modification::NonFinal) { + qCWarning(lcShiboken, "%s", + qPrintable(msgUnimplementedAttributeValueWarning(reader, + accessAttribute(), access))); + } + mod.modifiers |= m; + } + + if (deprecated) + mod.modifiers |= Modification::Deprecated; + + if (!removal.isEmpty() && !convertRemovalAttribute(removal, mod, m_error)) + return false; + + if (!rename.isEmpty()) { + mod.renamedToName = rename; + mod.modifiers |= Modification::Rename; + } + + if (!association.isEmpty()) + mod.association = association; + + mod.setIsThread(isThread); + if (allowThread != TypeSystem::AllowThread::Unspecified) + mod.setAllowThread(allowThread); + + m_contextStack.top()->functionMods << mod; + return true; +} + +bool Handler::parseReplaceDefaultExpression(const QXmlStreamReader &, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + if (!(topElement.type & StackElement::ModifyArgument)) { + m_error = QLatin1String("Replace default expression only allowed as child of argument modification"); + return false; + } + const int withIndex = indexOfAttribute(*attributes, QStringViewLiteral("with")); + if (withIndex == -1 || attributes->at(withIndex).value().isEmpty()) { + m_error = QLatin1String("Default expression replaced with empty string. Use remove-default-expression instead."); + return false; + } + + m_contextStack.top()->functionMods.last().argument_mods.last().replacedDefaultExpression = + attributes->takeAt(withIndex).value().toString(); + return true; +} + +CustomFunction * + Handler::parseCustomMetaConstructor(const QXmlStreamReader &, + StackElement::ElementType type, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + QString functionName = topElement.entry->name().toLower() + + (type == StackElement::CustomMetaConstructor + ? QLatin1String("_create") : QLatin1String("_delete")); + QString paramName = QLatin1String("copy"); + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == nameAttribute()) + functionName = attributes->takeAt(i).value().toString(); + else if (name == QLatin1String("param-name")) + paramName = attributes->takeAt(i).value().toString(); + } + CustomFunction *func = new CustomFunction(functionName); + func->paramName = paramName; + return func; +} + +bool Handler::parseReferenceCount(const QXmlStreamReader &reader, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + if (topElement.type != StackElement::ModifyArgument) { + m_error = QLatin1String("reference-count must be child of modify-argument"); + return false; + } + + ReferenceCount rc; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == actionAttribute()) { + const QXmlStreamAttribute attribute = attributes->takeAt(i); + rc.action = referenceCountFromAttribute(attribute.value()); + switch (rc.action) { + case ReferenceCount::Invalid: + m_error = QLatin1String("unrecognized value '") + attribute.value() + + QLatin1String("' for action attribute."); + return false; + case ReferenceCount::AddAll: + case ReferenceCount::Ignore: + qCWarning(lcShiboken, "%s", + qPrintable(msgUnimplementedAttributeValueWarning(reader, attribute))); + break; + default: + break; + } + } else if (name == QLatin1String("variable-name")) { + rc.varName = attributes->takeAt(i).value().toString(); + } + } + + m_contextStack.top()->functionMods.last().argument_mods.last().referenceCounts.append(rc); + return true; +} + +bool Handler::parseParentOwner(const QXmlStreamReader &, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + if (topElement.type != StackElement::ModifyArgument) { + m_error = QLatin1String("parent-policy must be child of modify-argument"); + return false; + } + ArgumentOwner ao; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == indexAttribute()) { + const QString index = attributes->takeAt(i).value().toString(); + if (!parseArgumentIndex(index, &ao.index, &m_error)) + return false; + } else if (name == actionAttribute()) { + const QStringRef action = attributes->takeAt(i).value(); + ao.action = argumentOwnerActionFromAttribute(action); + if (ao.action == ArgumentOwner::Invalid) { + m_error = QLatin1String("Invalid parent actionr '") + action + QLatin1String("'."); + return false; + } + } + } + m_contextStack.top()->functionMods.last().argument_mods.last().owner = ao; + return true; +} + +bool Handler::parseInjectCode(const QXmlStreamReader &, + const StackElement &topElement, + StackElement* element, QXmlStreamAttributes *attributes) +{ + if (!(topElement.type & StackElement::ComplexTypeEntryMask) + && (topElement.type != StackElement::AddFunction) + && (topElement.type != StackElement::ModifyFunction) + && (topElement.type != StackElement::Root)) { + m_error = QLatin1String("wrong parent type for code injection"); + return false; + } + + TypeSystem::CodeSnipPosition position = TypeSystem::CodeSnipPositionBeginning; + TypeSystem::Language lang = TypeSystem::TargetLangCode; + QString fileName; + QString snippetLabel; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == classAttribute()) { + const QStringRef className = attributes->takeAt(i).value(); + lang = languageFromAttribute(className); + if (lang == TypeSystem::NoLanguage) { + m_error = QStringLiteral("Invalid class specifier: '%1'").arg(className); + return false; + } + } else if (name == positionAttribute()) { + const QStringRef value = attributes->takeAt(i).value(); + position = codeSnipPositionFromAttribute(value); + if (position == TypeSystem::CodeSnipPositionInvalid) { + m_error = QStringLiteral("Invalid position: '%1'").arg(value); + return false; + } + } else if (name == QLatin1String("file")) { + fileName = attributes->takeAt(i).value().toString(); + } else if (name == snippetAttribute()) { + snippetLabel = attributes->takeAt(i).value().toString(); + } + } + + CodeSnip snip; + snip.position = position; + snip.language = lang; + bool in_file = false; + + // Handler constructor.... + if (m_generate != TypeEntry::GenerateForSubclass && + m_generate != TypeEntry::GenerateNothing && + !fileName.isEmpty()) { + const QString resolved = m_database->modifiedTypesystemFilepath(fileName, m_currentPath); + if (QFile::exists(resolved)) { + QFile codeFile(resolved); + if (codeFile.open(QIODevice::Text | QIODevice::ReadOnly)) { + QString content = QLatin1String("// ========================================================================\n" + "// START of custom code block [file: "); + content += fileName; + content += QLatin1String("]\n"); + content += extractSnippet(QString::fromUtf8(codeFile.readAll()), snippetLabel); + content += QLatin1String("\n// END of custom code block [file: "); + content += fileName; + content += QLatin1String("]\n// ========================================================================\n"); + snip.addCode(content); + in_file = true; + } + } else { + qCWarning(lcShiboken).noquote().nospace() + << "File for inject code not exist: " << QDir::toNativeSeparators(fileName); + } + + } + + if (snip.language == TypeSystem::Interface + && topElement.type != StackElement::InterfaceTypeEntry) { + m_error = QLatin1String("Interface code injections must be direct child of an interface type entry"); + return false; + } + + if (topElement.type == StackElement::ModifyFunction + || topElement.type == StackElement::AddFunction) { + if (snip.language == TypeSystem::ShellDeclaration) { + m_error = QLatin1String("no function implementation in shell declaration in which to inject code"); + return false; + } + + FunctionModification &mod = m_contextStack.top()->functionMods.last(); + mod.snips << snip; + if (in_file) + mod.modifiers |= FunctionModification::CodeInjection; + element->type = StackElement::InjectCodeInFunction; + } else if (topElement.type == StackElement::Root) { + element->entry->addCodeSnip(snip); + } else if (topElement.type != StackElement::Root) { + m_contextStack.top()->codeSnips << snip; + } + return true; +} + +bool Handler::parseInclude(const QXmlStreamReader &, + const StackElement &topElement, + TypeEntry *entry, QXmlStreamAttributes *attributes) +{ + QString fileName; + QString location; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == QLatin1String("file-name")) + fileName = attributes->takeAt(i).value().toString(); + else if (name == locationAttribute()) + location = attributes->takeAt(i).value().toString(); + } + const Include::IncludeType loc = locationFromAttribute(location); + if (loc == Include::InvalidInclude) { + m_error = QStringLiteral("Location not recognized: '%1'").arg(location); + return false; + } + + Include inc(loc, fileName); + if (topElement.type + & (StackElement::ComplexTypeEntryMask | StackElement::PrimitiveTypeEntry)) { + entry->setInclude(inc); + } else if (topElement.type == StackElement::ExtraIncludes) { + entry->addExtraInclude(inc); + } else { + m_error = QLatin1String("Only supported parent tags are primitive-type, complex types or extra-includes"); + return false; + } + if (InterfaceTypeEntry *di = entry->designatedInterface()) { + di->setInclude(entry->include()); + di->setExtraIncludes(entry->extraIncludes()); + } + return true; +} + +TemplateInstance * + Handler::parseTemplateInstanceEnum(const QXmlStreamReader &, + const StackElement &topElement, + QXmlStreamAttributes *attributes) +{ + if (!(topElement.type & StackElement::CodeSnipMask) && + (topElement.type != StackElement::Template) && + (topElement.type != StackElement::CustomMetaConstructor) && + (topElement.type != StackElement::CustomMetaDestructor) && + (topElement.type != StackElement::NativeToTarget) && + (topElement.type != StackElement::AddConversion) && + (topElement.type != StackElement::ConversionRule)) { + m_error = QLatin1String("Can only insert templates into code snippets, templates, custom-constructors, "\ + "custom-destructors, conversion-rule, native-to-target or add-conversion tags."); + return nullptr; + } + const int nameIndex = indexOfAttribute(*attributes, nameAttribute()); + if (nameIndex == -1) { + m_error = msgMissingAttribute(nameAttribute()); + return nullptr; + } + return new TemplateInstance(attributes->takeAt(nameIndex).value().toString()); +} + +bool Handler::parseReplace(const QXmlStreamReader &, + const StackElement &topElement, + StackElement *element, QXmlStreamAttributes *attributes) +{ + if (topElement.type != StackElement::TemplateInstanceEnum) { + m_error = QLatin1String("Can only insert replace rules into insert-template."); + return false; + } + QString from; + QString to; + for (int i = attributes->size() - 1; i >= 0; --i) { + const QStringRef name = attributes->at(i).qualifiedName(); + if (name == QLatin1String("from")) + from = attributes->takeAt(i).value().toString(); + else if (name == toAttribute()) + to = attributes->takeAt(i).value().toString(); + } + element->parent->value.templateInstance->addReplaceRule(from, to); + return true; +} + +bool Handler::startElement(const QXmlStreamReader &reader) { if (m_ignoreDepth) { ++m_ignoreDepth; return true; } + const QStringRef tagName = reader.name(); + QXmlStreamAttributes attributes = reader.attributes(); + QVersionNumber since(0, 0); - const QStringRef sinceSpec = atts.value(sinceAttribute()); - if (!sinceSpec.isNull()) { + int index = indexOfAttribute(attributes, sinceAttribute()); + if (index != -1) { + const QStringRef sinceSpec = attributes.takeAt(index).value(); since = QVersionNumber::fromString(sinceSpec.toString()); if (since.isNull()) { m_error = msgInvalidVersion(sinceSpec, m_defaultPackage); @@ -722,12 +2419,11 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts } } - const QString tagName = n.toString().toLower(); - if (tagName == QLatin1String("import-file")) - return importFileElement(atts); + if (tagName.compare(QLatin1String("import-file"), Qt::CaseInsensitive) == 0) + return importFileElement(attributes); - const QHash<QString, StackElement::ElementType>::const_iterator tit = tagNames.constFind(tagName); - if (tit == tagNames.constEnd()) { + const StackElement::ElementType elementType = elementFromTag(tagName); + if (elementType == StackElement::None) { m_error = QStringLiteral("Unknown tag name: '%1'").arg(tagName); return false; } @@ -738,92 +2434,48 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts } StackElement* element = new StackElement(m_current); - element->type = tit.value(); + element->type = elementType; if (element->type == StackElement::Root && m_generate == TypeEntry::GenerateAll) customConversionsForReview.clear(); - if (element->type == StackElement::Root - || element->type == StackElement::NamespaceTypeEntry - || element->type == StackElement::InterfaceTypeEntry - || element->type == StackElement::ObjectTypeEntry - || element->type == StackElement::ValueTypeEntry - || element->type == StackElement::PrimitiveTypeEntry) { + if (element->type == StackElement::CustomMetaConstructor + || element->type == StackElement::CustomMetaDestructor) { + qCWarning(lcShiboken, "%s", + qPrintable(msgUnimplementedElementWarning(reader, tagName))); + } + + switch (element->type) { + case StackElement::Root: + case StackElement::NamespaceTypeEntry: + case StackElement::InterfaceTypeEntry: + case StackElement::ObjectTypeEntry: + case StackElement::ValueTypeEntry: + case StackElement::PrimitiveTypeEntry: + case StackElement::TypedefTypeEntry: m_contextStack.push(new StackElementContext()); + break; + default: + break; } if (element->type & StackElement::TypeEntryMask) { - QHash<QString, QString> attributes; - attributes.insert(nameAttribute(), QString()); - attributes.insert(QLatin1String("revision"), QLatin1String("0")); - attributes.insert(sinceAttribute(), QString()); // dummy for matching allowed attributes - - switch (element->type) { - case StackElement::PrimitiveTypeEntry: - attributes.insert(QLatin1String("target-lang-name"), QString()); - attributes.insert(QLatin1String("target-lang-api-name"), QString()); - attributes.insert(QLatin1String("preferred-conversion"), yesAttributeValue()); - attributes.insert(QLatin1String("preferred-target-lang-type"), yesAttributeValue()); - attributes.insert(QLatin1String("default-constructor"), QString()); - break; - case StackElement::ContainerTypeEntry: - attributes.insert(QLatin1String("type"), QString()); - break; - case StackElement::SmartPointerTypeEntry: - attributes.insert(QLatin1String("type"), QString()); - attributes.insert(QLatin1String("getter"), QString()); - attributes.insert(QLatin1String("ref-count-method"), QString()); - break; - case StackElement::EnumTypeEntry: - attributes.insert(flagsAttribute(), QString()); - attributes.insert(QLatin1String("flags-revision"), QString()); - attributes.insert(QLatin1String("upper-bound"), QString()); - attributes.insert(QLatin1String("lower-bound"), QString()); - attributes.insert(QLatin1String("force-integer"), noAttributeValue()); - attributes.insert(QLatin1String("extensible"), noAttributeValue()); - attributes.insert(enumIdentifiedByValueAttribute(), QString()); - attributes.insert(classAttribute(), falseAttributeValue()); - break; - case StackElement::ValueTypeEntry: - attributes.insert(QLatin1String("default-constructor"), QString()); - Q_FALLTHROUGH(); - case StackElement::ObjectTypeEntry: - attributes.insert(QLatin1String("force-abstract"), noAttributeValue()); - attributes.insert(QLatin1String("deprecated"), noAttributeValue()); - attributes.insert(QLatin1String("hash-function"), QString()); - attributes.insert(QLatin1String("stream"), noAttributeValue()); - Q_FALLTHROUGH(); - case StackElement::InterfaceTypeEntry: - attributes[QLatin1String("default-superclass")] = m_defaultSuperclass; - attributes.insert(QLatin1String("polymorphic-id-expression"), QString()); - attributes.insert(QLatin1String("delete-in-main-thread"), noAttributeValue()); - attributes.insert(QLatin1String("held-type"), QString()); - attributes.insert(QLatin1String("copyable"), QString()); - Q_FALLTHROUGH(); - case StackElement::NamespaceTypeEntry: - attributes.insert(QLatin1String("target-lang-name"), QString()); - attributes[QLatin1String("package")] = m_defaultPackage; - attributes.insert(QLatin1String("expense-cost"), QLatin1String("1")); - attributes.insert(QLatin1String("expense-limit"), QLatin1String("none")); - attributes.insert(QLatin1String("polymorphic-base"), noAttributeValue()); - attributes.insert(QLatin1String("generate"), yesAttributeValue()); - attributes.insert(QLatin1String("target-type"), QString()); - attributes.insert(QLatin1String("generic-class"), noAttributeValue()); - break; - case StackElement::FunctionTypeEntry: - attributes.insert(QLatin1String("signature"), QString()); - attributes.insert(QLatin1String("rename"), QString()); - break; - default: - { } // nada - }; - - fetchAttributeValues(tagName, atts, &attributes); - QString name = attributes[nameAttribute()]; + QString name; + if (element->type != StackElement::FunctionTypeEntry) { + const int nameIndex = indexOfAttribute(attributes, nameAttribute()); + if (nameIndex != -1) { + name = attributes.takeAt(nameIndex).value().toString(); + } else if (element->type != StackElement::EnumTypeEntry) { // anonymous enum? + m_error = msgMissingAttribute(nameAttribute()); + return false; + } + } if (m_database->hasDroppedTypeEntries()) { QString identifier = getNamePrefix(element) + QLatin1Char('.'); - identifier += (element->type == StackElement::FunctionTypeEntry ? attributes[QLatin1String("signature")] : name); + identifier += element->type == StackElement::FunctionTypeEntry + ? attributes.value(signatureAttribute()).toString() + : name; if (m_database->shouldDropTypeEntry(identifier)) { m_currentDroppedEntry = element; m_currentDroppedEntryDepth = 1; @@ -837,30 +2489,9 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts // The top level tag 'function' has only the 'signature' tag // and we should extract the 'name' value from it. - if (element->type == StackElement::FunctionTypeEntry) { - QString signature = attributes[QLatin1String("signature")]; - name = signature.left(signature.indexOf(QLatin1Char('('))).trimmed(); - QString errorString = checkSignatureError(signature, QLatin1String("function")); - if (!errorString.isEmpty()) { - m_error = errorString; + if (element->type == StackElement::FunctionTypeEntry + && !parseRenameFunction(reader, &name, &attributes)) { return false; - } - QString rename = attributes[QLatin1String("rename")]; - if (!rename.isEmpty()) { - static const QRegularExpression functionNameRegExp(QLatin1String("^[a-zA-Z_][a-zA-Z0-9_]*$")); - Q_ASSERT(functionNameRegExp.isValid()); - if (!functionNameRegExp.match(rename).hasMatch()) { - m_error = QLatin1String("can not rename '") + signature + QLatin1String("', '") - + rename + QLatin1String("' is not a valid function name"); - return false; - } - FunctionModification mod; - if (!mod.setSignature(signature, &m_error)) - return false; - mod.renamedToName = attributes[QLatin1String("rename")]; - mod.modifiers |= Modification::Rename; - m_contextStack.top()->functionMods << mod; - } } // We need to be able to have duplicate primitive type entries, @@ -875,7 +2506,9 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts } if (element->type == StackElement::EnumTypeEntry) { - const QString identifiedByValue = attributes.value(enumIdentifiedByValueAttribute()); + const int enumIdentifiedByIndex = indexOfAttribute(attributes, enumIdentifiedByValueAttribute()); + const QString identifiedByValue = enumIdentifiedByIndex != -1 + ? attributes.takeAt(enumIdentifiedByIndex).value().toString() : QString(); if (name.isEmpty()) { name = identifiedByValue; } else if (!identifiedByValue.isEmpty()) { @@ -900,281 +2533,92 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts case StackElement::CustomTypeEntry: element->entry = new TypeEntry(name, TypeEntry::CustomType, since); break; - case StackElement::PrimitiveTypeEntry: { - QString targetLangName = attributes[QLatin1String("target-lang-name")]; - QString targetLangApiName = attributes[QLatin1String("target-lang-api-name")]; - QString preferredConversion = attributes[QLatin1String("preferred-conversion")].toLower(); - QString preferredTargetLangType = attributes[QLatin1String("preferred-target-lang-type")].toLower(); - QString defaultConstructor = attributes[QLatin1String("default-constructor")]; - - if (targetLangName.isEmpty()) - targetLangName = name; - if (targetLangApiName.isEmpty()) - targetLangApiName = name; - - PrimitiveTypeEntry *type = new PrimitiveTypeEntry(name, since); - type->setCodeGeneration(m_generate); - type->setTargetLangName(targetLangName); - type->setTargetLangApiName(targetLangApiName); - type->setTargetLangPackage(m_defaultPackage); - type->setDefaultConstructor(defaultConstructor); - - bool preferred; - preferred = convertBoolean(preferredConversion, QLatin1String("preferred-conversion"), true); - type->setPreferredConversion(preferred); - preferred = convertBoolean(preferredTargetLangType, - QLatin1String("preferred-target-lang-type"), true); - type->setPreferredTargetLangType(preferred); - - element->entry = type; - } - break; - - case StackElement::ContainerTypeEntry: { - QString typeName = attributes[QLatin1String("type")]; - ContainerTypeEntry::Type containerType = - ContainerTypeEntry::containerTypeFromString(typeName); - if (typeName.isEmpty()) { - m_error = QLatin1String("no 'type' attribute specified"); + case StackElement::PrimitiveTypeEntry: + element->entry = parsePrimitiveTypeEntry(reader, name, since, &attributes); + if (Q_UNLIKELY(!element->entry)) return false; - } else if (containerType == ContainerTypeEntry::NoContainer) { - m_error = QLatin1String("there is no container of type ") + typeName; + break; + case StackElement::ContainerTypeEntry: + if (ContainerTypeEntry *ce = parseContainerTypeEntry(reader, name, since, &attributes)) { + applyComplexTypeAttributes(reader, ce, &attributes); + element->entry = ce; + } else { return false; } + break; - ContainerTypeEntry *type = new ContainerTypeEntry(name, containerType, since); - type->setCodeGeneration(m_generate); - element->entry = type; - } - break; - - case StackElement::SmartPointerTypeEntry: { - bool result = handleSmartPointerEntry(element, attributes, name, since); - if (!result) - return result; - } - break; - - case StackElement::EnumTypeEntry: { - QStringList names = name.split(colonColon()); - if (names.size() == 1) - m_currentEnum = new EnumTypeEntry(QString(), name, since); - else - m_currentEnum = - new EnumTypeEntry(QStringList(names.mid(0, names.size() - 1)).join(colonColon()), - names.constLast(), since); - element->entry = m_currentEnum; - m_currentEnum->setCodeGeneration(m_generate); - m_currentEnum->setTargetLangPackage(m_defaultPackage); - m_currentEnum->setUpperBound(attributes[QLatin1String("upper-bound")]); - m_currentEnum->setLowerBound(attributes[QLatin1String("lower-bound")]); - m_currentEnum->setForceInteger(convertBoolean(attributes[QLatin1String("force-integer")], QLatin1String("force-integer"), false)); - m_currentEnum->setExtensible(convertBoolean(attributes[QLatin1String("extensible")], QLatin1String("extensible"), false)); - - // put in the flags parallel... - const QString flagNames = attributes.value(flagsAttribute()); - if (!flagNames.isEmpty()) { - const QStringList &flagNameList = flagNames.split(QLatin1Char(',')); - for (const QString &flagName : flagNameList) - addFlags(name, flagName.trimmed(), attributes, since); + case StackElement::SmartPointerTypeEntry: + if (SmartPointerTypeEntry *se = parseSmartPointerEntry(reader, name, since, &attributes)) { + applyComplexTypeAttributes(reader, se, &attributes); + element->entry = se; + } else { + return false; } - } - break; + break; + case StackElement::EnumTypeEntry: + m_currentEnum = parseEnumTypeEntry(reader, name, since, &attributes); + if (Q_UNLIKELY(!m_currentEnum)) + return false; + element->entry = m_currentEnum; + break; - case StackElement::InterfaceTypeEntry: { - ObjectTypeEntry *otype = new ObjectTypeEntry(name, since); - QString targetLangName = attributes[QLatin1String("target-lang-name")]; - if (targetLangName.isEmpty()) - targetLangName = name; - InterfaceTypeEntry *itype = - new InterfaceTypeEntry(InterfaceTypeEntry::interfaceName(targetLangName), since); - - if (!convertBoolean(attributes[QLatin1String("generate")], QLatin1String("generate"), true)) - itype->setCodeGeneration(TypeEntry::GenerateForSubclass); - else - itype->setCodeGeneration(m_generate); - otype->setDesignatedInterface(itype); - itype->setOrigin(otype); - element->entry = otype; - } - Q_FALLTHROUGH(); - case StackElement::ValueTypeEntry: { - if (!element->entry) { - ValueTypeEntry* typeEntry = new ValueTypeEntry(name, since); - QString defaultConstructor = attributes[QLatin1String("default-constructor")]; - if (!defaultConstructor.isEmpty()) - typeEntry->setDefaultConstructor(defaultConstructor); - element->entry = typeEntry; + case StackElement::InterfaceTypeEntry: + if (ObjectTypeEntry *oe = parseInterfaceTypeEntry(reader, name, since, &attributes)) { + applyComplexTypeAttributes(reader, oe, &attributes); + element->entry = oe; + } else { + return false; } - - Q_FALLTHROUGH(); + break; + case StackElement::ValueTypeEntry: + if (ValueTypeEntry *ve = parseValueTypeEntry(reader, name, since, &attributes)) { + applyComplexTypeAttributes(reader, ve, &attributes); + element->entry = ve; + } else { + return false; + } + break; case StackElement::NamespaceTypeEntry: - if (!element->entry) - element->entry = new NamespaceTypeEntry(name, since); - - Q_FALLTHROUGH(); + element->entry = new NamespaceTypeEntry(name, since); + applyCommonAttributes(element->entry, &attributes); + applyComplexTypeAttributes(reader, static_cast<ComplexTypeEntry *>(element->entry), &attributes); + break; case StackElement::ObjectTypeEntry: - if (!element->entry) - element->entry = new ObjectTypeEntry(name, since); - - element->entry->setStream(attributes[QLatin1String("stream")] == yesAttributeValue()); - - ComplexTypeEntry *ctype = static_cast<ComplexTypeEntry *>(element->entry); - ctype->setTargetLangPackage(attributes[QLatin1String("package")]); - ctype->setDefaultSuperclass(attributes[QLatin1String("default-superclass")]); - ctype->setGenericClass(convertBoolean(attributes[QLatin1String("generic-class")], QLatin1String("generic-class"), false)); - - if (!convertBoolean(attributes[QLatin1String("generate")], QLatin1String("generate"), true)) - element->entry->setCodeGeneration(TypeEntry::GenerateForSubclass); - else - element->entry->setCodeGeneration(m_generate); - - QString targetLangName = attributes[QLatin1String("target-lang-name")]; - if (!targetLangName.isEmpty()) - ctype->setTargetLangName(targetLangName); - - ctype->setIsPolymorphicBase(convertBoolean(attributes[QLatin1String("polymorphic-base")], QLatin1String("polymorphic-base"), false)); - ctype->setPolymorphicIdValue(attributes[QLatin1String("polymorphic-id-expression")]); - //Copyable - if (attributes[QLatin1String("copyable")].isEmpty()) - ctype->setCopyable(ComplexTypeEntry::Unknown); - else { - if (convertBoolean(attributes[QLatin1String("copyable")], QLatin1String("copyable"), false)) - ctype->setCopyable(ComplexTypeEntry::CopyableSet); - else - ctype->setCopyable(ComplexTypeEntry::NonCopyableSet); - - } - - if (element->type == StackElement::ObjectTypeEntry || element->type == StackElement::ValueTypeEntry) - ctype->setHashFunction(attributes[QLatin1String("hash-function")]); - - - ctype->setHeldType(attributes[QLatin1String("held-type")]); - - if (element->type == StackElement::ObjectTypeEntry - || element->type == StackElement::ValueTypeEntry) { - if (convertBoolean(attributes[QLatin1String("force-abstract")], QLatin1String("force-abstract"), false)) - ctype->setTypeFlags(ctype->typeFlags() | ComplexTypeEntry::ForceAbstract); - if (convertBoolean(attributes[QLatin1String("deprecated")], QLatin1String("deprecated"), false)) - ctype->setTypeFlags(ctype->typeFlags() | ComplexTypeEntry::Deprecated); - } - - if (element->type == StackElement::InterfaceTypeEntry - || element->type == StackElement::ValueTypeEntry - || element->type == StackElement::ObjectTypeEntry) { - if (convertBoolean(attributes[QLatin1String("delete-in-main-thread")], QLatin1String("delete-in-main-thread"), false)) - ctype->setTypeFlags(ctype->typeFlags() | ComplexTypeEntry::DeleteInMainThread); - } - - QString targetType = attributes[QLatin1String("target-type")]; - if (!targetType.isEmpty() && element->entry->isComplex()) - static_cast<ComplexTypeEntry *>(element->entry)->setTargetType(targetType); - - // ctype->setInclude(Include(Include::IncludePath, ctype->name())); - ctype = ctype->designatedInterface(); - if (ctype) - ctype->setTargetLangPackage(attributes[QLatin1String("package")]); - - } - break; - case StackElement::FunctionTypeEntry: { - QString signature = attributes[QLatin1String("signature")]; - signature = TypeDatabase::normalizedSignature(signature); - element->entry = m_database->findType(name); - if (element->entry) { - if (element->entry->type() == TypeEntry::FunctionType) { - reinterpret_cast<FunctionTypeEntry*>(element->entry)->addSignature(signature); - } else { - m_error = QStringLiteral("%1 expected to be a function, but isn't! Maybe it was already declared as a class or something else.") - .arg(name); - return false; - } + element->entry = new ObjectTypeEntry(name, since); + applyCommonAttributes(element->entry, &attributes); + applyComplexTypeAttributes(reader, static_cast<ComplexTypeEntry *>(element->entry), &attributes); + break; + case StackElement::FunctionTypeEntry: + element->entry = parseFunctionTypeEntry(reader, name, since, &attributes); + if (Q_UNLIKELY(!element->entry)) + return false; + break; + case StackElement::TypedefTypeEntry: + if (TypedefEntry *te = parseTypedefEntry(reader, name, since, &attributes)) { + applyComplexTypeAttributes(reader, te, &attributes); + element->entry = te; } else { - element->entry = new FunctionTypeEntry(name, signature, since); - element->entry->setCodeGeneration(m_generate); + return false; } - } - break; + break; default: Q_ASSERT(false); }; if (element->entry) { - m_database->addType(element->entry); - setTypeRevision(element->entry, attributes[QLatin1String("revision")].toInt()); + if (!m_database->addType(element->entry, &m_error)) + return false; } else { qCWarning(lcShiboken).noquote().nospace() << QStringLiteral("Type: %1 was rejected by typesystem").arg(name); } } else if (element->type == StackElement::InjectDocumentation) { - // check the XML tag attributes - QHash<QString, QString> attributes; - attributes.insert(QLatin1String("mode"), QLatin1String("replace")); - attributes.insert(QLatin1String("format"), QLatin1String("native")); - attributes.insert(sinceAttribute(), QString()); // dummy for matching allowed attributes - - fetchAttributeValues(tagName, atts, &attributes); - - const int validParent = StackElement::TypeEntryMask - | StackElement::ModifyFunction - | StackElement::ModifyField; - if (m_current->parent && m_current->parent->type & validParent) { - QString modeName = attributes[QLatin1String("mode")]; - TypeSystem::DocModificationMode mode; - if (modeName == QLatin1String("append")) { - mode = TypeSystem::DocModificationAppend; - } else if (modeName == QLatin1String("prepend")) { - mode = TypeSystem::DocModificationPrepend; - } else if (modeName == QLatin1String("replace")) { - mode = TypeSystem::DocModificationReplace; - } else { - m_error = QLatin1String("Unknow documentation injection mode: ") + modeName; - return false; - } - - static QHash<QString, TypeSystem::Language> languageNames; - if (languageNames.isEmpty()) { - languageNames[QLatin1String("target")] = TypeSystem::TargetLangCode; - languageNames[QLatin1String("native")] = TypeSystem::NativeCode; - } - - QString format = attributes[QLatin1String("format")].toLower(); - TypeSystem::Language lang = languageNames.value(format, TypeSystem::NoLanguage); - if (lang == TypeSystem::NoLanguage) { - m_error = QStringLiteral("unsupported class attribute: '%1'").arg(format); - return false; - } - - QString signature = m_current->type & StackElement::TypeEntryMask ? QString() : m_currentSignature; - DocModification mod(mode, signature); - mod.setFormat(lang); - m_contextStack.top()->docModifications << mod; - } else { - m_error = QLatin1String("inject-documentation must be inside modify-function, " - "modify-field or other tags that creates a type"); + if (!parseInjectDocumentation(reader, &attributes)) return false; - } } else if (element->type == StackElement::ModifyDocumentation) { - // check the XML tag attributes - QHash<QString, QString> attributes; - attributes.insert(xPathAttribute(), QString()); - attributes.insert(sinceAttribute(), QString()); // dummy for matching allowed attributes - fetchAttributeValues(tagName, atts, &attributes); - - const int validParent = StackElement::TypeEntryMask - | StackElement::ModifyFunction - | StackElement::ModifyField; - if (m_current->parent && m_current->parent->type & validParent) { - QString signature = (m_current->type & StackElement::TypeEntryMask) ? QString() : m_currentSignature; - m_contextStack.top()->docModifications - << DocModification(attributes.value(xPathAttribute()), signature); - } else { - m_error = QLatin1String("modify-documentation must be inside modify-function, " - "modify-field or other tags that creates a type"); + if (!parseModifyDocumentation(reader, &attributes)) return false; - } } else if (element->type != StackElement::None) { bool topLevel = element->type == StackElement::Root || element->type == StackElement::SuppressedWarning @@ -1194,494 +2638,89 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts StackElement topElement = !m_current ? StackElement(0) : *m_current; element->entry = topElement.entry; - QHash<QString, QString> attributes; - attributes.insert(sinceAttribute(), QString()); // dummy for matching allowed attributes switch (element->type) { case StackElement::Root: - attributes.insert(QLatin1String("package"), QString()); - attributes.insert(QLatin1String("default-superclass"), QString()); + element->entry = parseRootElement(reader, since, &attributes); + element->type = StackElement::Root; break; case StackElement::LoadTypesystem: - attributes.insert(nameAttribute(), QString()); - attributes.insert(QLatin1String("generate"), yesAttributeValue()); - break; - case StackElement::NoNullPointers: - attributes.insert(QLatin1String("default-value"), QString()); - break; - case StackElement::SuppressedWarning: - attributes.insert(textAttribute(), QString()); - break; - case StackElement::ReplaceDefaultExpression: - attributes.insert(QLatin1String("with"), QString()); - break; - case StackElement::DefineOwnership: - attributes.insert(QLatin1String("class"), QLatin1String("target")); - attributes.insert(QLatin1String("owner"), QString()); - break; - case StackElement::AddFunction: - attributes.insert(QLatin1String("signature"), QString()); - attributes.insert(QLatin1String("return-type"), QLatin1String("void")); - attributes.insert(QLatin1String("access"), QLatin1String("public")); - attributes.insert(QLatin1String("static"), noAttributeValue()); - break; - case StackElement::ModifyFunction: - attributes.insert(QLatin1String("signature"), QString()); - attributes.insert(QLatin1String("access"), QString()); - attributes.insert(QLatin1String("remove"), QString()); - attributes.insert(QLatin1String("rename"), QString()); - attributes.insert(QLatin1String("deprecated"), noAttributeValue()); - attributes.insert(QLatin1String("associated-to"), QString()); - attributes.insert(QLatin1String("virtual-slot"), noAttributeValue()); - attributes.insert(QLatin1String("thread"), noAttributeValue()); - attributes.insert(QLatin1String("allow-thread"), noAttributeValue()); - break; - case StackElement::ModifyArgument: - attributes.insert(QLatin1String("index"), QString()); - attributes.insert(QLatin1String("replace-value"), QString()); - attributes.insert(QLatin1String("invalidate-after-use"), noAttributeValue()); - break; - case StackElement::ModifyField: - attributes.insert(nameAttribute(), QString()); - attributes.insert(QLatin1String("write"), trueAttributeValue()); - attributes.insert(QLatin1String("read"), trueAttributeValue()); - attributes.insert(QLatin1String("remove"), QString()); - break; - case StackElement::Access: - attributes.insert(QLatin1String("modifier"), QString()); - break; - case StackElement::Include: - attributes.insert(QLatin1String("file-name"), QString()); - attributes.insert(QLatin1String("location"), QString()); - break; - case StackElement::CustomMetaConstructor: - attributes[nameAttribute()] = topElement.entry->name().toLower() + QLatin1String("_create"); - attributes.insert(QLatin1String("param-name"), QLatin1String("copy")); + if (!loadTypesystem(reader, &attributes)) + return false; break; - case StackElement::CustomMetaDestructor: - attributes[nameAttribute()] = topElement.entry->name().toLower() + QLatin1String("_delete"); - attributes.insert(QLatin1String("param-name"), QLatin1String("copy")); + case StackElement::RejectEnumValue: + if (!parseRejectEnumValue(reader, &attributes)) + return false; break; case StackElement::ReplaceType: - attributes.insert(QLatin1String("modified-type"), QString()); - break; - case StackElement::InjectCode: - attributes.insert(QLatin1String("class"), QLatin1String("target")); - attributes.insert(QLatin1String("position"), QLatin1String("beginning")); - attributes.insert(QLatin1String("file"), QString()); + if (!parseReplaceArgumentType(reader, topElement, &attributes)) + return false; break; case StackElement::ConversionRule: - attributes.insert(QLatin1String("class"), QString()); - attributes.insert(QLatin1String("file"), QString()); - break; - case StackElement::TargetToNative: - attributes.insert(QLatin1String("replace"), yesAttributeValue()); - break; - case StackElement::AddConversion: - attributes.insert(QLatin1String("type"), QString()); - attributes.insert(QLatin1String("check"), QString()); - break; - case StackElement::RejectEnumValue: - attributes.insert(nameAttribute(), QString()); - break; - case StackElement::ArgumentMap: - attributes.insert(QLatin1String("index"), QLatin1String("1")); - attributes.insert(QLatin1String("meta-name"), QString()); - break; - case StackElement::Rename: - attributes.insert(QLatin1String("to"), QString()); - break; - case StackElement::Rejection: - attributes.insert(classAttribute(), QString()); - attributes.insert(functionNameAttribute(), QString()); - attributes.insert(fieldNameAttribute(), QString()); - attributes.insert(enumNameAttribute(), QString()); - attributes.insert(argumentTypeAttribute(), QString()); - attributes.insert(returnTypeAttribute(), QString()); - break; - case StackElement::Removal: - attributes.insert(QLatin1String("class"), QLatin1String("all")); - break; - case StackElement::Template: - attributes.insert(nameAttribute(), QString()); - break; - case StackElement::TemplateInstanceEnum: - attributes.insert(nameAttribute(), QString()); - break; - case StackElement::Replace: - attributes.insert(QLatin1String("from"), QString()); - attributes.insert(QLatin1String("to"), QString()); - break; - case StackElement::ReferenceCount: - attributes.insert(QLatin1String("action"), QString()); - attributes.insert(QLatin1String("variable-name"), QString()); - break; - case StackElement::ParentOwner: - attributes.insert(QLatin1String("index"), QString()); - attributes.insert(QLatin1String("action"), QString()); - break; - case StackElement::Array: - break; - default: - { }; - }; - - if (!attributes.isEmpty()) - fetchAttributeValues(tagName, atts, &attributes); - - switch (element->type) { - case StackElement::Root: - m_defaultPackage = attributes[QLatin1String("package")]; - m_defaultSuperclass = attributes[QLatin1String("default-superclass")]; - element->type = StackElement::Root; - { - TypeSystemTypeEntry* moduleEntry = reinterpret_cast<TypeSystemTypeEntry*>( - m_database->findType(m_defaultPackage)); - element->entry = moduleEntry ? moduleEntry : new TypeSystemTypeEntry(m_defaultPackage, since); - element->entry->setCodeGeneration(m_generate); - } - - if ((m_generate == TypeEntry::GenerateForSubclass || - m_generate == TypeEntry::GenerateNothing) && !m_defaultPackage.isEmpty()) - TypeDatabase::instance()->addRequiredTargetImport(m_defaultPackage); - - if (!element->entry->qualifiedCppName().isEmpty()) - m_database->addType(element->entry); - break; - case StackElement::LoadTypesystem: { - QString name = attributes[nameAttribute()]; - if (name.isEmpty()) { - m_error = QLatin1String("No typesystem name specified"); - return false; - } - bool generateChild = (convertBoolean(attributes[QLatin1String("generate")], QLatin1String("generate"), true) && (m_generate == TypeEntry::GenerateAll)); - if (!m_database->parseFile(name, m_currentPath, generateChild)) { - m_error = QStringLiteral("Failed to parse: '%1'").arg(name); - return false; - } - } - break; - case StackElement::RejectEnumValue: - if (!m_currentEnum) { - m_error = QLatin1String("<reject-enum-value> node must be used inside a <enum-type> node"); - return false; - } - break; - case StackElement::ReplaceType: { - if (topElement.type != StackElement::ModifyArgument) { - m_error = QLatin1String("Type replacement can only be specified for argument modifications"); - return false; - } - - if (attributes[QLatin1String("modified-type")].isEmpty()) { - m_error = QLatin1String("Type replacement requires 'modified-type' attribute"); + if (!Handler::parseCustomConversion(reader, topElement, &attributes)) return false; - } - - m_contextStack.top()->functionMods.last().argument_mods.last().modified_type = attributes[QLatin1String("modified-type")]; - } - break; - case StackElement::ConversionRule: { - if (topElement.type != StackElement::ModifyArgument - && topElement.type != StackElement::ValueTypeEntry - && topElement.type != StackElement::PrimitiveTypeEntry - && topElement.type != StackElement::ContainerTypeEntry) { - m_error = QLatin1String("Conversion rules can only be specified for argument modification, " - "value-type, primitive-type or container-type conversion."); - return false; - } - - static QHash<QString, TypeSystem::Language> languageNames; - if (languageNames.isEmpty()) { - languageNames[QLatin1String("target")] = TypeSystem::TargetLangCode; - languageNames[QLatin1String("native")] = TypeSystem::NativeCode; - } - - QString languageAttribute = attributes[QLatin1String("class")].toLower(); - TypeSystem::Language lang = languageNames.value(languageAttribute, TypeSystem::NoLanguage); - - if (topElement.type == StackElement::ModifyArgument) { - if (lang == TypeSystem::NoLanguage) { - m_error = QStringLiteral("unsupported class attribute: '%1'").arg(lang); - return false; - } - - CodeSnip snip; - snip.language = lang; - m_contextStack.top()->functionMods.last().argument_mods.last().conversion_rules.append(snip); - } else { - if (topElement.entry->hasConversionRule() || topElement.entry->hasCustomConversion()) { - m_error = QLatin1String("Types can have only one conversion rule"); - return false; - } - - // The old conversion rule tag that uses a file containing the conversion - // will be kept temporarily for compatibility reasons. - QString sourceFile = attributes[QLatin1String("file")]; - if (!sourceFile.isEmpty()) { - if (m_generate != TypeEntry::GenerateForSubclass - && m_generate != TypeEntry::GenerateNothing) { - - const char* conversionFlag = NATIVE_CONVERSION_RULE_FLAG; - if (lang == TypeSystem::TargetLangCode) - conversionFlag = TARGET_CONVERSION_RULE_FLAG; - - QFile conversionSource(sourceFile); - if (conversionSource.open(QIODevice::ReadOnly | QIODevice::Text)) { - topElement.entry->setConversionRule(QLatin1String(conversionFlag) + QString::fromUtf8(conversionSource.readAll())); - } else { - qCWarning(lcShiboken).noquote().nospace() - << "File containing conversion code for " - << topElement.entry->name() << " type does not exist or is not readable: " - << sourceFile; - } - } - } - - CustomConversion* customConversion = new CustomConversion(static_cast<TypeEntry*>(m_current->entry)); - customConversionsForReview.append(customConversion); - } - } - break; - case StackElement::NativeToTarget: { + break; + case StackElement::NativeToTarget: if (topElement.type != StackElement::ConversionRule) { m_error = QLatin1String("Native to Target conversion code can only be specified for custom conversion rules."); return false; } m_contextStack.top()->codeSnips << CodeSnip(); - } - break; + break; case StackElement::TargetToNative: { if (topElement.type != StackElement::ConversionRule) { m_error = QLatin1String("Target to Native conversions can only be specified for custom conversion rules."); return false; } - bool replace = attributes[QLatin1String("replace")] == yesAttributeValue(); - static_cast<TypeEntry*>(m_current->entry)->customConversion()->setReplaceOriginalTargetToNativeConversions(replace); + const int replaceIndex = indexOfAttribute(attributes, replaceAttribute()); + const bool replace = replaceIndex == -1 + || convertBoolean(attributes.takeAt(replaceIndex).value(), + replaceAttribute(), true); + m_current->entry->customConversion()->setReplaceOriginalTargetToNativeConversions(replace); } break; - case StackElement::AddConversion: { - if (topElement.type != StackElement::TargetToNative) { - m_error = QLatin1String("Target to Native conversions can only be added inside 'target-to-native' tags."); - return false; - } - QString sourceTypeName = attributes[QLatin1String("type")]; - if (sourceTypeName.isEmpty()) { - m_error = QLatin1String("Target to Native conversions must specify the input type with the 'type' attribute."); - return false; - } - QString typeCheck = attributes[QLatin1String("check")]; - static_cast<TypeEntry*>(m_current->entry)->customConversion()->addTargetToNativeConversion(sourceTypeName, typeCheck); - m_contextStack.top()->codeSnips << CodeSnip(); - } - break; - case StackElement::ModifyArgument: { - if (topElement.type != StackElement::ModifyFunction - && topElement.type != StackElement::AddFunction) { - m_error = QString::fromLatin1("argument modification requires function" - " modification as parent, was %1") - .arg(topElement.type, 0, 16); - return false; - } - - QString index = attributes[QLatin1String("index")]; - if (index == QLatin1String("return")) - index = QLatin1String("0"); - else if (index == QLatin1String("this")) - index = QLatin1String("-1"); - - bool ok = false; - int idx = index.toInt(&ok); - if (!ok) { - m_error = QStringLiteral("Cannot convert '%1' to integer").arg(index); - return false; - } - - QString replace_value = attributes[QLatin1String("replace-value")]; - - if (!replace_value.isEmpty() && idx) { - m_error = QLatin1String("replace-value is only supported for return values (index=0)."); - return false; - } - - ArgumentModification argumentModification = ArgumentModification(idx); - argumentModification.replace_value = replace_value; - argumentModification.resetAfterUse = convertBoolean(attributes[QLatin1String("invalidate-after-use")], QLatin1String("invalidate-after-use"), false); - m_contextStack.top()->functionMods.last().argument_mods.append(argumentModification); - } - break; - case StackElement::NoNullPointers: { - if (topElement.type != StackElement::ModifyArgument) { - m_error = QLatin1String("no-null-pointer requires argument modification as parent"); + case StackElement::AddConversion: + if (!parseAddConversion(reader, topElement, &attributes)) return false; - } - - m_contextStack.top()->functionMods.last().argument_mods.last().noNullPointers = true; - if (!m_contextStack.top()->functionMods.last().argument_mods.last().index) - m_contextStack.top()->functionMods.last().argument_mods.last().nullPointerDefaultValue = attributes[QLatin1String("default-value")]; - else if (!attributes[QLatin1String("default-value")].isEmpty()) - qCWarning(lcShiboken) << "default values for null pointer guards are only effective for return values"; - - } - break; - case StackElement::DefineOwnership: { - if (topElement.type != StackElement::ModifyArgument) { - m_error = QLatin1String("define-ownership requires argument modification as parent"); + break; + case StackElement::ModifyArgument: + if (!parseModifyArgument(reader, topElement, &attributes)) return false; - } - - static QHash<QString, TypeSystem::Language> languageNames; - if (languageNames.isEmpty()) { - languageNames[QLatin1String("target")] = TypeSystem::TargetLangCode; - languageNames[QLatin1String("native")] = TypeSystem::NativeCode; - } - - QString classAttribute = attributes[QLatin1String("class")].toLower(); - TypeSystem::Language lang = languageNames.value(classAttribute, TypeSystem::NoLanguage); - if (lang == TypeSystem::NoLanguage) { - m_error = QStringLiteral("unsupported class attribute: '%1'").arg(classAttribute); + break; + case StackElement::NoNullPointers: + if (!parseNoNullPointer(reader, topElement, &attributes)) return false; - } - - static QHash<QString, TypeSystem::Ownership> ownershipNames; - if (ownershipNames.isEmpty()) { - ownershipNames[QLatin1String("target")] = TypeSystem::TargetLangOwnership; - ownershipNames[QLatin1String("c++")] = TypeSystem::CppOwnership; - ownershipNames[QLatin1String("default")] = TypeSystem::DefaultOwnership; - } - - QString ownershipAttribute = attributes[QLatin1String("owner")].toLower(); - TypeSystem::Ownership owner = ownershipNames.value(ownershipAttribute, TypeSystem::InvalidOwnership); - if (owner == TypeSystem::InvalidOwnership) { - m_error = QStringLiteral("unsupported owner attribute: '%1'").arg(ownershipAttribute); + break; + case StackElement::DefineOwnership: + if (!parseDefineOwnership(reader, topElement, &attributes)) return false; - } - - m_contextStack.top()->functionMods.last().argument_mods.last().ownerships[lang] = owner; - } - break; + break; case StackElement::SuppressedWarning: { - const QString suppressedWarning = attributes.value(textAttribute()); - if (suppressedWarning.isEmpty()) { + const int textIndex = indexOfAttribute(attributes, textAttribute()); + if (textIndex == -1) { qCWarning(lcShiboken) << "Suppressed warning with no text specified"; } else { + const QString suppressedWarning = + attributes.takeAt(textIndex).value().toString(); if (!m_database->addSuppressedWarning(suppressedWarning, &m_error)) return false; } } break; - case StackElement::ArgumentMap: { - if (!(topElement.type & StackElement::CodeSnipMask)) { - m_error = QLatin1String("Argument maps requires code injection as parent"); - return false; - } - - bool ok; - int pos = attributes[QLatin1String("index")].toInt(&ok); - if (!ok) { - m_error = QStringLiteral("Can't convert position '%1' to integer") - .arg(attributes[QLatin1String("position")]); - return false; - } - - if (pos <= 0) { - m_error = QStringLiteral("Argument position %1 must be a positive number").arg(pos); - return false; - } - - QString meta_name = attributes[QLatin1String("meta-name")]; - if (meta_name.isEmpty()) - qCWarning(lcShiboken) << "Empty meta name in argument map"; - - - if (topElement.type == StackElement::InjectCodeInFunction) - m_contextStack.top()->functionMods.last().snips.last().argumentMap[pos] = meta_name; - else { - qCWarning(lcShiboken) << "Argument maps are only useful for injection of code " - "into functions."; - } - } - break; - case StackElement::Removal: { - if (topElement.type != StackElement::ModifyFunction) { - m_error = QLatin1String("Function modification parent required"); + case StackElement::ArgumentMap: + qCWarning(lcShiboken, "%s", + qPrintable(msgUnimplementedElementWarning(reader, tagName))); + if (!parseArgumentMap(reader, topElement, &attributes)) return false; - } - - static QHash<QString, TypeSystem::Language> languageNames; - if (languageNames.isEmpty()) { - languageNames.insert(QLatin1String("target"), TypeSystem::TargetLangAndNativeCode); - languageNames.insert(QLatin1String("all"), TypeSystem::All); - } - - QString languageAttribute = attributes[QLatin1String("class")].toLower(); - TypeSystem::Language lang = languageNames.value(languageAttribute, TypeSystem::NoLanguage); - if (lang == TypeSystem::NoLanguage) { - m_error = QStringLiteral("unsupported class attribute: '%1'").arg(languageAttribute); + break; + case StackElement::Removal: + if (!parseRemoval(reader, topElement, &attributes)) return false; - } - - m_contextStack.top()->functionMods.last().removal = lang; - } - break; + break; case StackElement::Rename: - case StackElement::Access: { - if (topElement.type != StackElement::ModifyField - && topElement.type != StackElement::ModifyFunction - && topElement.type != StackElement::ModifyArgument) { - m_error = QLatin1String("Function, field or argument modification parent required"); - return false; - } - - Modification *mod = 0; - if (topElement.type == StackElement::ModifyFunction) - mod = &m_contextStack.top()->functionMods.last(); - else if (topElement.type == StackElement::ModifyField) - mod = &m_contextStack.top()->fieldMods.last(); - - QString modifier; - if (element->type == StackElement::Rename) { - modifier = QLatin1String("rename"); - QString renamed_to = attributes[QLatin1String("to")]; - if (renamed_to.isEmpty()) { - m_error = QLatin1String("Rename modifier requires 'to' attribute"); - return false; - } - - if (topElement.type == StackElement::ModifyFunction) - mod->setRenamedTo(renamed_to); - else if (topElement.type == StackElement::ModifyField) - mod->setRenamedTo(renamed_to); - else - m_contextStack.top()->functionMods.last().argument_mods.last().renamed_to = renamed_to; - } else - modifier = attributes[QLatin1String("modifier")].toLower(); - - - if (modifier.isEmpty()) { - m_error = QLatin1String("No access modification specified"); - return false; - } - - static QHash<QString, FunctionModification::Modifiers> modifierNames; - if (modifierNames.isEmpty()) { - modifierNames[QLatin1String("private")] = Modification::Private; - modifierNames[QLatin1String("public")] = Modification::Public; - modifierNames[QLatin1String("protected")] = Modification::Protected; - modifierNames[QLatin1String("friendly")] = Modification::Friendly; - modifierNames[QLatin1String("rename")] = Modification::Rename; - modifierNames[QLatin1String("final")] = Modification::Final; - modifierNames[QLatin1String("non-final")] = Modification::NonFinal; - } - - if (!modifierNames.contains(modifier)) { - m_error = QStringLiteral("Unknown access modifier: '%1'").arg(modifier); + case StackElement::Access: + if (!parseRename(reader, element->type, topElement, &attributes)) return false; - } - - if (mod) - mod->modifiers |= modifierNames[modifier]; - } - break; + break; case StackElement::RemoveArgument: if (topElement.type != StackElement::ModifyArgument) { m_error = QLatin1String("Removing argument requires argument modification as parent"); @@ -1691,229 +2730,38 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts m_contextStack.top()->functionMods.last().argument_mods.last().removed = true; break; - case StackElement::ModifyField: { - QString name = attributes[nameAttribute()]; - if (name.isEmpty()) - break; - FieldModification fm; - fm.name = name; - fm.modifiers = 0; - - if (!convertRemovalAttribute(attributes[QLatin1String("remove")], fm, m_error)) - return false; - - QString read = attributes[QLatin1String("read")]; - QString write = attributes[QLatin1String("write")]; - - if (read == trueAttributeValue()) fm.modifiers |= FieldModification::Readable; - if (write == trueAttributeValue()) fm.modifiers |= FieldModification::Writable; - - m_contextStack.top()->fieldMods << fm; - } - break; - case StackElement::AddFunction: { - if (!(topElement.type & (StackElement::ComplexTypeEntryMask | StackElement::Root))) { - m_error = QString::fromLatin1("Add function requires a complex type or a root tag as parent" - ", was=%1").arg(topElement.type, 0, 16); - return false; - } - const QString originalSignature = attributes[QLatin1String("signature")]; - - QString signature = TypeDatabase::normalizedSignature(originalSignature); - if (signature.isEmpty()) { - m_error = QLatin1String("No signature for the added function"); - return false; - } - - QString errorString = checkSignatureError(signature, QLatin1String("add-function")); - if (!errorString.isEmpty()) { - m_error = errorString; - return false; - } - - AddedFunction func(signature, attributes[QLatin1String("return-type")]); - func.setStatic(attributes[QLatin1String("static")] == yesAttributeValue()); - if (!signature.contains(QLatin1Char('('))) - signature += QLatin1String("()"); - m_currentSignature = signature; - - QString access = attributes[QLatin1String("access")].toLower(); - if (!access.isEmpty()) { - if (access == QLatin1String("protected")) { - func.setAccess(AddedFunction::Protected); - } else if (access == QLatin1String("public")) { - func.setAccess(AddedFunction::Public); - } else { - m_error = QString::fromLatin1("Bad access type '%1'").arg(access); - return false; - } - } - - m_contextStack.top()->addedFunctions << func; - - FunctionModification mod; - if (!mod.setSignature(m_currentSignature, &m_error)) - return false; - mod.setOriginalSignature(originalSignature); - m_contextStack.top()->functionMods << mod; - } - break; - case StackElement::ModifyFunction: { - if (!(topElement.type & StackElement::ComplexTypeEntryMask)) { - m_error = QString::fromLatin1("Modify function requires complex type as parent" - ", was=%1").arg(topElement.type, 0, 16); - return false; - } - const QString originalSignature = attributes[QLatin1String("signature")]; - - const QString signature = TypeDatabase::normalizedSignature(originalSignature); - if (signature.isEmpty()) { - m_error = QLatin1String("No signature for modified function"); - return false; - } - - QString errorString = checkSignatureError(signature, QLatin1String("modify-function")); - if (!errorString.isEmpty()) { - m_error = errorString; + case StackElement::ModifyField: + if (!parseModifyField(reader, &attributes)) return false; - } - - FunctionModification mod; - if (!mod.setSignature(signature, &m_error)) + break; + case StackElement::AddFunction: + if (!parseAddFunction(reader, topElement, &attributes)) return false; - mod.setOriginalSignature(originalSignature); - m_currentSignature = signature; - - QString access = attributes[QLatin1String("access")].toLower(); - if (!access.isEmpty()) { - if (access == QLatin1String("private")) - mod.modifiers |= Modification::Private; - else if (access == QLatin1String("protected")) - mod.modifiers |= Modification::Protected; - else if (access == QLatin1String("public")) - mod.modifiers |= Modification::Public; - else if (access == QLatin1String("final")) - mod.modifiers |= Modification::Final; - else if (access == QLatin1String("non-final")) - mod.modifiers |= Modification::NonFinal; - else { - m_error = QString::fromLatin1("Bad access type '%1'").arg(access); - return false; - } - } - - if (convertBoolean(attributes[QLatin1String("deprecated")], QLatin1String("deprecated"), false)) - mod.modifiers |= Modification::Deprecated; - - if (!convertRemovalAttribute(attributes[QLatin1String("remove")], mod, m_error)) + break; + case StackElement::ModifyFunction: + if (!parseModifyFunction(reader, topElement, &attributes)) return false; - - QString rename = attributes[QLatin1String("rename")]; - if (!rename.isEmpty()) { - mod.renamedToName = rename; - mod.modifiers |= Modification::Rename; - } - - QString association = attributes[QLatin1String("associated-to")]; - if (!association.isEmpty()) - mod.association = association; - - mod.setIsThread(convertBoolean(attributes[QLatin1String("thread")], QLatin1String("thread"), false)); - mod.setAllowThread(convertBoolean(attributes[QLatin1String("allow-thread")], QLatin1String("allow-thread"), false)); - - mod.modifiers |= (convertBoolean(attributes[QLatin1String("virtual-slot")], QLatin1String("virtual-slot"), false) ? Modification::VirtualSlot : 0); - - m_contextStack.top()->functionMods << mod; - } - break; + break; case StackElement::ReplaceDefaultExpression: - if (!(topElement.type & StackElement::ModifyArgument)) { - m_error = QLatin1String("Replace default expression only allowed as child of argument modification"); - return false; - } - - if (attributes[QLatin1String("with")].isEmpty()) { - m_error = QLatin1String("Default expression replaced with empty string. Use remove-default-expression instead."); + if (!parseReplaceDefaultExpression(reader, topElement, &attributes)) return false; - } - - m_contextStack.top()->functionMods.last().argument_mods.last().replacedDefaultExpression = attributes[QLatin1String("with")]; break; case StackElement::RemoveDefaultExpression: m_contextStack.top()->functionMods.last().argument_mods.last().removedDefaultExpression = true; break; case StackElement::CustomMetaConstructor: - case StackElement::CustomMetaDestructor: { - CustomFunction *func = new CustomFunction(attributes[nameAttribute()]); - func->paramName = attributes[QLatin1String("param-name")]; - element->value.customFunction = func; - } - break; - case StackElement::ReferenceCount: { - if (topElement.type != StackElement::ModifyArgument) { - m_error = QLatin1String("reference-count must be child of modify-argument"); - return false; - } - - ReferenceCount rc; - - static QHash<QString, ReferenceCount::Action> actions; - if (actions.isEmpty()) { - actions[QLatin1String("add")] = ReferenceCount::Add; - actions[QLatin1String("add-all")] = ReferenceCount::AddAll; - actions[QLatin1String("remove")] = ReferenceCount::Remove; - actions[QLatin1String("set")] = ReferenceCount::Set; - actions[QLatin1String("ignore")] = ReferenceCount::Ignore; - } - rc.action = actions.value(attributes[QLatin1String("action")].toLower(), ReferenceCount::Invalid); - rc.varName = attributes[QLatin1String("variable-name")]; - - if (rc.action == ReferenceCount::Invalid) { - m_error = QLatin1String("unrecognized value for action attribute. supported actions:"); - for (QHash<QString, ReferenceCount::Action>::const_iterator it = actions.cbegin(), end = actions.cend(); it != end; ++it) - m_error += QLatin1Char(' ') + it.key(); - } - - m_contextStack.top()->functionMods.last().argument_mods.last().referenceCounts.append(rc); - } - break; - - case StackElement::ParentOwner: { - if (topElement.type != StackElement::ModifyArgument) { - m_error = QLatin1String("parent-policy must be child of modify-argument"); - return false; - } - - ArgumentOwner ao; - - QString index = attributes[QLatin1String("index")]; - if (index == QLatin1String("return")) - index = QLatin1String("0"); - else if (index == QLatin1String("this")) - index = QLatin1String("-1"); - - bool ok = false; - int idx = index.toInt(&ok); - if (!ok) { - m_error = QStringLiteral("Cannot convert '%1' to integer").arg(index); + case StackElement::CustomMetaDestructor: + element->value.customFunction = + parseCustomMetaConstructor(reader, element->type, topElement, &attributes); + break; + case StackElement::ReferenceCount: + if (!parseReferenceCount(reader, topElement, &attributes)) return false; - } - - static QHash<QString, ArgumentOwner::Action> actions; - if (actions.isEmpty()) { - actions[QLatin1String("add")] = ArgumentOwner::Add; - actions[QLatin1String("remove")] = ArgumentOwner::Remove; - } - - ao.action = actions.value(attributes[QLatin1String("action")].toLower(), ArgumentOwner::Invalid); - if (!ao.action) { - m_error = QLatin1String("Invalid parent actionr"); + break; + case StackElement::ParentOwner: + if (!parseParentOwner(reader, topElement, &attributes)) return false; - } - ao.index = idx; - m_contextStack.top()->functionMods.last().argument_mods.last().owner = ao; - } - break; + break; case StackElement::Array: if (topElement.type != StackElement::ModifyArgument) { m_error = QLatin1String("array must be child of modify-argument"); @@ -1921,185 +2769,54 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts } m_contextStack.top()->functionMods.last().argument_mods.last().array = true; break; - case StackElement::InjectCode: { - if (!(topElement.type & StackElement::ComplexTypeEntryMask) - && (topElement.type != StackElement::AddFunction) - && (topElement.type != StackElement::ModifyFunction) - && (topElement.type != StackElement::Root)) { - m_error = QLatin1String("wrong parent type for code injection"); - return false; - } - - static QHash<QString, TypeSystem::Language> languageNames; - if (languageNames.isEmpty()) { - languageNames[QLatin1String("target")] = TypeSystem::TargetLangCode; // em algum lugar do cpp - languageNames[QLatin1String("native")] = TypeSystem::NativeCode; // em algum lugar do cpp - languageNames[QLatin1String("shell")] = TypeSystem::ShellCode; // coloca no header, mas antes da declaracao da classe - languageNames[QLatin1String("shell-declaration")] = TypeSystem::ShellDeclaration; // coloca no header, dentro da declaracao da classe - languageNames[QLatin1String("library-initializer")] = TypeSystem::PackageInitializer; - languageNames[QLatin1String("destructor-function")] = TypeSystem::DestructorFunction; - languageNames[QLatin1String("constructors")] = TypeSystem::Constructors; - languageNames[QLatin1String("interface")] = TypeSystem::Interface; - } - - QString className = attributes[QLatin1String("class")].toLower(); - if (!languageNames.contains(className)) { - m_error = QStringLiteral("Invalid class specifier: '%1'").arg(className); - return false; - } - - - static QHash<QString, TypeSystem::CodeSnipPosition> positionNames; - if (positionNames.isEmpty()) { - positionNames.insert(QLatin1String("beginning"), TypeSystem::CodeSnipPositionBeginning); - positionNames.insert(QLatin1String("end"), TypeSystem::CodeSnipPositionEnd); - // QtScript - positionNames.insert(QLatin1String("declaration"), TypeSystem::CodeSnipPositionDeclaration); - positionNames.insert(QLatin1String("prototype-initialization"), TypeSystem::CodeSnipPositionPrototypeInitialization); - positionNames.insert(QLatin1String("constructor-initialization"), TypeSystem::CodeSnipPositionConstructorInitialization); - positionNames.insert(QLatin1String("constructor"), TypeSystem::CodeSnipPositionConstructor); - } - - QString position = attributes[QLatin1String("position")].toLower(); - if (!positionNames.contains(position)) { - m_error = QStringLiteral("Invalid position: '%1'").arg(position); + case StackElement::InjectCode: + if (!parseInjectCode(reader, topElement, element, &attributes)) return false; - } - - CodeSnip snip; - snip.language = languageNames[className]; - snip.position = positionNames[position]; - bool in_file = false; - - QString file_name = attributes[QLatin1String("file")]; - - //Handler constructor.... - if (m_generate != TypeEntry::GenerateForSubclass && - m_generate != TypeEntry::GenerateNothing && - !file_name.isEmpty()) { - const QString resolved = m_database->modifiedTypesystemFilepath(file_name, m_currentPath); - if (QFile::exists(resolved)) { - QFile codeFile(resolved); - if (codeFile.open(QIODevice::Text | QIODevice::ReadOnly)) { - QString content = QLatin1String("// ========================================================================\n" - "// START of custom code block [file: "); - content += file_name; - content += QLatin1String("]\n"); - content += QString::fromUtf8(codeFile.readAll()); - content += QLatin1String("\n// END of custom code block [file: "); - content += file_name; - content += QLatin1String("]\n// ========================================================================\n"); - snip.addCode(content); - in_file = true; - } - } else { - qCWarning(lcShiboken).noquote().nospace() - << "File for inject code not exist: " << QDir::toNativeSeparators(file_name); - } - - } - - if (snip.language == TypeSystem::Interface && topElement.type != StackElement::InterfaceTypeEntry) { - m_error = QLatin1String("Interface code injections must be direct child of an interface type entry"); + break; + case StackElement::Include: + if (!parseInclude(reader, topElement, element->entry, &attributes)) return false; - } - - if (topElement.type == StackElement::ModifyFunction || topElement.type == StackElement::AddFunction) { - FunctionModification mod = m_contextStack.top()->functionMods.constLast(); - if (snip.language == TypeSystem::ShellDeclaration) { - m_error = QLatin1String("no function implementation in shell declaration in which to inject code"); - return false; - } - - m_contextStack.top()->functionMods.last().snips << snip; - if (in_file) - m_contextStack.top()->functionMods.last().modifiers |= FunctionModification::CodeInjection; - element->type = StackElement::InjectCodeInFunction; - } else if (topElement.type == StackElement::Root) { - element->entry->addCodeSnip(snip); - } else if (topElement.type != StackElement::Root) { - m_contextStack.top()->codeSnips << snip; - } - - } - break; - case StackElement::Include: { - QString location = attributes[QLatin1String("location")].toLower(); - - static QHash<QString, Include::IncludeType> locationNames; - if (locationNames.isEmpty()) { - locationNames[QLatin1String("global")] = Include::IncludePath; - locationNames[QLatin1String("local")] = Include::LocalPath; - locationNames[QLatin1String("target")] = Include::TargetLangImport; - } - - if (!locationNames.contains(location)) { - m_error = QStringLiteral("Location not recognized: '%1'").arg(location); + break; + case StackElement::Rejection: + if (!addRejection(m_database, &attributes, &m_error)) return false; - } - - Include::IncludeType loc = locationNames[location]; - Include inc(loc, attributes[QLatin1String("file-name")]); - - ComplexTypeEntry *ctype = static_cast<ComplexTypeEntry *>(element->entry); - if (topElement.type & (StackElement::ComplexTypeEntryMask | StackElement::PrimitiveTypeEntry)) { - element->entry->setInclude(inc); - } else if (topElement.type == StackElement::ExtraIncludes) { - element->entry->addExtraInclude(inc); - } else { - m_error = QLatin1String("Only supported parent tags are primitive-type, complex types or extra-includes"); + break; + case StackElement::Template: { + const int nameIndex = indexOfAttribute(attributes, nameAttribute()); + if (nameIndex == -1) { + m_error = msgMissingAttribute(nameAttribute()); return false; } - - inc = ctype->include(); - IncludeList lst = ctype->extraIncludes(); - ctype = ctype->designatedInterface(); - if (ctype) { - ctype->setExtraIncludes(lst); - ctype->setInclude(inc); - } + element->value.templateEntry = + new TemplateEntry(attributes.takeAt(nameIndex).value().toString()); } - break; - case StackElement::Rejection: - if (!addRejection(m_database, attributes, &m_error)) - return false; - break; - case StackElement::Template: - element->value.templateEntry = new TemplateEntry(attributes.value(nameAttribute())); break; case StackElement::TemplateInstanceEnum: - if (!(topElement.type & StackElement::CodeSnipMask) && - (topElement.type != StackElement::Template) && - (topElement.type != StackElement::CustomMetaConstructor) && - (topElement.type != StackElement::CustomMetaDestructor) && - (topElement.type != StackElement::NativeToTarget) && - (topElement.type != StackElement::AddConversion) && - (topElement.type != StackElement::ConversionRule)) { - m_error = QLatin1String("Can only insert templates into code snippets, templates, custom-constructors, "\ - "custom-destructors, conversion-rule, native-to-target or add-conversion tags."); + element->value.templateInstance = + parseTemplateInstanceEnum(reader, topElement, &attributes); + if (!element->value.templateInstance) return false; - } - element->value.templateInstance = new TemplateInstance(attributes.value(nameAttribute())); break; case StackElement::Replace: - if (topElement.type != StackElement::TemplateInstanceEnum) { - m_error = QLatin1String("Can only insert replace rules into insert-template."); + if (!parseReplace(reader, topElement, element, &attributes)) return false; - } - element->parent->value.templateInstance->addReplaceRule(attributes[QLatin1String("from")], attributes[QLatin1String("to")]); break; default: break; // nada }; } + if (!attributes.isEmpty()) { + const QString message = msgUnusedAttributes(tagName, attributes); + qCWarning(lcShiboken, "%s", qPrintable(msgReaderWarning(reader, message))); + } + m_current = element; return true; } PrimitiveTypeEntry::PrimitiveTypeEntry(const QString &name, const QVersionNumber &vr) : TypeEntry(name, PrimitiveType, vr), - m_preferredConversion(true), m_preferredTargetLangType(true) { } @@ -2120,35 +2837,15 @@ PrimitiveTypeEntry *PrimitiveTypeEntry::basicReferencedTypeEntry() const return 0; PrimitiveTypeEntry *baseReferencedTypeEntry = m_referencedTypeEntry->basicReferencedTypeEntry(); - if (baseReferencedTypeEntry) - return baseReferencedTypeEntry; - else - return m_referencedTypeEntry; -} - -bool PrimitiveTypeEntry::preferredConversion() const -{ - return m_preferredConversion; + return baseReferencedTypeEntry ? baseReferencedTypeEntry : m_referencedTypeEntry; } -void PrimitiveTypeEntry::setPreferredConversion(bool b) +TypeEntry *PrimitiveTypeEntry::clone() const { - m_preferredConversion = b; + return new PrimitiveTypeEntry(*this); } -typedef QHash<const PrimitiveTypeEntry*, QString> PrimitiveTypeEntryTargetLangPackageMap; -Q_GLOBAL_STATIC(PrimitiveTypeEntryTargetLangPackageMap, primitiveTypeEntryTargetLangPackages); - -void PrimitiveTypeEntry::setTargetLangPackage(const QString& package) -{ - primitiveTypeEntryTargetLangPackages()->insert(this, package); -} -QString PrimitiveTypeEntry::targetLangPackage() const -{ - if (!primitiveTypeEntryTargetLangPackages()->contains(this)) - return this->::TypeEntry::targetLangPackage(); - return primitiveTypeEntryTargetLangPackages()->value(this); -} +PrimitiveTypeEntry::PrimitiveTypeEntry(const PrimitiveTypeEntry &) = default; CodeSnipList TypeEntry::codeSnips() const { @@ -2186,42 +2883,42 @@ FieldModification ComplexTypeEntry::fieldModification(const QString &name) const return mod; } -QString ComplexTypeEntry::targetLangPackage() const -{ - return m_package; -} - QString ComplexTypeEntry::targetLangName() const { return m_targetLangName.isEmpty() ? TypeEntry::targetLangName() : m_targetLangName; } -// The things we do not to break the ABI... -typedef QHash<const ComplexTypeEntry*, QString> ComplexTypeEntryDefaultConstructorMap; -Q_GLOBAL_STATIC(ComplexTypeEntryDefaultConstructorMap, complexTypeEntryDefaultConstructors); - void ComplexTypeEntry::setDefaultConstructor(const QString& defaultConstructor) { - if (!defaultConstructor.isEmpty()) - complexTypeEntryDefaultConstructors()->insert(this, defaultConstructor); + m_defaultConstructor = defaultConstructor; } QString ComplexTypeEntry::defaultConstructor() const { - if (!complexTypeEntryDefaultConstructors()->contains(this)) - return QString(); - return complexTypeEntryDefaultConstructors()->value(this); + return m_defaultConstructor; } bool ComplexTypeEntry::hasDefaultConstructor() const { - return complexTypeEntryDefaultConstructors()->contains(this); + return !m_defaultConstructor.isEmpty(); } -QString ContainerTypeEntry::targetLangPackage() const +TypeEntry *ComplexTypeEntry::clone() const { - return QString(); + return new ComplexTypeEntry(*this); } +// Take over parameters relevant for typedefs +void ComplexTypeEntry::useAsTypedef(const ComplexTypeEntry *source) +{ + TypeEntry::useAsTypedef(source); + m_qualifiedCppName = source->m_qualifiedCppName; + m_targetLangName = source->m_targetLangName; + m_lookupName = source->m_lookupName; + m_targetType = source->m_targetType; +} + +ComplexTypeEntry::ComplexTypeEntry(const ComplexTypeEntry &) = default; + QString ContainerTypeEntry::targetLangName() const { @@ -2252,13 +2949,17 @@ QString ContainerTypeEntry::qualifiedCppName() const return ComplexTypeEntry::qualifiedCppName(); } +TypeEntry *ContainerTypeEntry::clone() const +{ + return new ContainerTypeEntry(*this); +} + +ContainerTypeEntry::ContainerTypeEntry(const ContainerTypeEntry &) = default; + QString EnumTypeEntry::targetLangQualifier() const { TypeEntry *te = TypeDatabase::instance()->findType(m_qualifier); - if (te) - return te->targetLangName(); - else - return m_qualifier; + return te ? te->targetLangName() : m_qualifier; } QString EnumTypeEntry::qualifiedTargetLangName() const @@ -2281,26 +2982,25 @@ QString EnumTypeEntry::targetLangApiName() const return QLatin1String("jint"); } -bool EnumTypeEntry::preferredConversion() const -{ - return false; -} - QString FlagsTypeEntry::targetLangApiName() const { return QLatin1String("jint"); } -bool FlagsTypeEntry::preferredConversion() const +TypeEntry *EnumTypeEntry::clone() const { - return false; + return new EnumTypeEntry(*this); } -QString FlagsTypeEntry::targetLangPackage() const +EnumTypeEntry::EnumTypeEntry(const EnumTypeEntry &) = default; + +TypeEntry *FlagsTypeEntry::clone() const { - return m_enum->targetLangPackage(); + return new FlagsTypeEntry(*this); } +FlagsTypeEntry::FlagsTypeEntry(const FlagsTypeEntry &) = default; + QString FlagsTypeEntry::qualifiedTargetLangName() const { return targetLangPackage() + QLatin1Char('.') + m_enum->targetLangQualifier() @@ -2320,7 +3020,7 @@ QString fixCppTypeName(const QString &name) { if (name == QLatin1String("long long")) return QLatin1String("qint64"); - else if (name == QLatin1String("unsigned long long")) + if (name == QLatin1String("unsigned long long")) return QLatin1String("quint64"); return name; } @@ -2341,11 +3041,9 @@ QString TemplateInstance::expandCode() const result += code; result += QLatin1String("\n// TEMPLATE - ") + m_name + QLatin1String(" - END"); return result; - } else { - qCWarning(lcShiboken).noquote().nospace() - << "insert-template referring to non-existing template '" << m_name << '\''; } - + qCWarning(lcShiboken).noquote().nospace() + << "insert-template referring to non-existing template '" << m_name << '\''; return QString(); } @@ -2361,10 +3059,7 @@ QString CodeSnipAbstract::code() const QString CodeSnipFragment::code() const { - if (m_instance) - return m_instance->expandCode(); - else - return m_code; + return m_instance ? m_instance->expandCode() : m_code; } bool FunctionModification::setSignature(const QString &s, QString *errorMessage) @@ -2556,11 +3251,12 @@ AddedFunction::TypeInfo AddedFunction::TypeInfo::fromSignature(const QString& si ComplexTypeEntry::ComplexTypeEntry(const QString &name, TypeEntry::Type t, const QVersionNumber &vr) : - TypeEntry(QString(name).replace(QLatin1String(".*::"), QString()), t, vr), + TypeEntry(name, t, vr), m_qualifiedCppName(name), m_qobject(false), m_polymorphicBase(false), - m_genericClass(false) + m_genericClass(false), + m_deleteInMainThread(false) { } @@ -2578,71 +3274,6 @@ QString ComplexTypeEntry::targetLangApiName() const { return strings_jobject; } -QString StringTypeEntry::targetLangApiName() const -{ - return strings_jobject; -} -QString StringTypeEntry::targetLangName() const -{ - return strings_String; -} -QString StringTypeEntry::targetLangPackage() const -{ - return QString(); -} - -bool StringTypeEntry::isNativeIdBased() const -{ - return false; -} - -CharTypeEntry::CharTypeEntry(const QString &name, const QVersionNumber &vr) : - ValueTypeEntry(name, CharType, vr) -{ - setCodeGeneration(GenerateNothing); -} - -QString CharTypeEntry::targetLangApiName() const -{ - return strings_jchar; -} -QString CharTypeEntry::targetLangName() const -{ - return strings_char; -} - -QString CharTypeEntry::targetLangPackage() const -{ - return QString(); -} - -bool CharTypeEntry::isNativeIdBased() const -{ - return false; -} - -VariantTypeEntry::VariantTypeEntry(const QString &name, const QVersionNumber &vr) : - ValueTypeEntry(name, VariantType, vr) -{ -} - -QString VariantTypeEntry::targetLangApiName() const -{ - return strings_jobject; -} -QString VariantTypeEntry::targetLangName() const -{ - return strings_Object; -} -QString VariantTypeEntry::targetLangPackage() const -{ - return QString(); -} - -bool VariantTypeEntry::isNativeIdBased() const -{ - return false; -} QString ContainerTypeEntry::typeName() const { @@ -2706,10 +3337,6 @@ bool TypeEntry::isCppPrimitive() const return typeName.contains(QLatin1Char(' ')) || primitiveCppTypes().contains(typeName); } -// Again, stuff to avoid ABI breakage. -typedef QHash<const TypeEntry*, CustomConversion*> TypeEntryCustomConversionMap; -Q_GLOBAL_STATIC(TypeEntryCustomConversionMap, typeEntryCustomConversionMap); - TypeEntry::TypeEntry(const QString &name, TypeEntry::Type t, const QVersionNumber &vr) : m_name(name), m_type(t), @@ -2719,51 +3346,88 @@ TypeEntry::TypeEntry(const QString &name, TypeEntry::Type t, const QVersionNumbe TypeEntry::~TypeEntry() { - if (typeEntryCustomConversionMap()->contains(this)) { - CustomConversion* customConversion = typeEntryCustomConversionMap()->value(this); - typeEntryCustomConversionMap()->remove(this); - delete customConversion; - } + delete m_customConversion; } bool TypeEntry::hasCustomConversion() const { - return typeEntryCustomConversionMap()->contains(this); + return m_customConversion != nullptr; } + void TypeEntry::setCustomConversion(CustomConversion* customConversion) { - if (customConversion) - typeEntryCustomConversionMap()->insert(this, customConversion); - else if (typeEntryCustomConversionMap()->contains(this)) - typeEntryCustomConversionMap()->remove(this); + m_customConversion = customConversion; } + CustomConversion* TypeEntry::customConversion() const { - if (typeEntryCustomConversionMap()->contains(this)) - return typeEntryCustomConversionMap()->value(this); - return 0; + return m_customConversion; +} + +TypeEntry *TypeEntry::clone() const +{ + return new TypeEntry(*this); } +// Take over parameters relevant for typedefs +void TypeEntry::useAsTypedef(const TypeEntry *source) +{ + m_name = source->m_name; + m_targetLangPackage = source->m_targetLangPackage; + m_codeGeneration = source->m_codeGeneration; + m_version = source->m_version; +} + +TypeEntry::TypeEntry(const TypeEntry &) = default; + TypeSystemTypeEntry::TypeSystemTypeEntry(const QString &name, const QVersionNumber &vr) : TypeEntry(name, TypeSystemType, vr) { } +TypeEntry *TypeSystemTypeEntry::clone() const +{ + return new TypeSystemTypeEntry(*this); +} + +TypeSystemTypeEntry::TypeSystemTypeEntry(const TypeSystemTypeEntry &) = default; + VoidTypeEntry::VoidTypeEntry() : TypeEntry(QLatin1String("void"), VoidType, QVersionNumber(0, 0)) { } +TypeEntry *VoidTypeEntry::clone() const +{ + return new VoidTypeEntry(*this); +} + +VoidTypeEntry::VoidTypeEntry(const VoidTypeEntry &) = default; + VarargsTypeEntry::VarargsTypeEntry() : TypeEntry(QLatin1String("..."), VarargsType, QVersionNumber(0, 0)) { } +TypeEntry *VarargsTypeEntry::clone() const +{ + return new VarargsTypeEntry(*this); +} + +VarargsTypeEntry::VarargsTypeEntry(const VarargsTypeEntry &) = default; + TemplateArgumentEntry::TemplateArgumentEntry(const QString &name, const QVersionNumber &vr) : TypeEntry(name, TemplateArgumentType, vr) { } +TypeEntry *TemplateArgumentEntry::clone() const +{ + return new TemplateArgumentEntry(*this); +} + +TemplateArgumentEntry::TemplateArgumentEntry(const TemplateArgumentEntry &) = default; + ArrayTypeEntry::ArrayTypeEntry(const TypeEntry *nested_type, const QVersionNumber &vr) : TypeEntry(QLatin1String("Array"), ArrayType, vr), m_nestedType(nested_type) @@ -2778,12 +3442,18 @@ QString ArrayTypeEntry::targetLangName() const QString ArrayTypeEntry::targetLangApiName() const { - if (m_nestedType->isPrimitive()) - return m_nestedType->targetLangApiName() + QLatin1String("Array"); - else - return QLatin1String("jobjectArray"); + return m_nestedType->isPrimitive() + ? m_nestedType->targetLangApiName() + QLatin1String("Array") + : QLatin1String("jobjectArray"); +} + +TypeEntry *ArrayTypeEntry::clone() const +{ + return new ArrayTypeEntry(*this); } +ArrayTypeEntry::ArrayTypeEntry(const ArrayTypeEntry &) = default; + EnumTypeEntry::EnumTypeEntry(const QString &nspace, const QString &enumName, const QVersionNumber &vr) : TypeEntry(nspace.isEmpty() ? enumName : nspace + QLatin1String("::") + enumName, @@ -2793,16 +3463,6 @@ EnumTypeEntry::EnumTypeEntry(const QString &nspace, const QString &enumName, { } -QString EnumTypeEntry::targetLangPackage() const -{ - return m_packageName; -} - -void EnumTypeEntry::setTargetLangPackage(const QString &package) -{ - m_packageName = package; -} - QString EnumTypeEntry::targetLangName() const { return m_targetLangName; @@ -2817,11 +3477,34 @@ EnumValueTypeEntry::EnumValueTypeEntry(const QString &name, const QString &value { } +TypeEntry *EnumValueTypeEntry::clone() const +{ + return new EnumValueTypeEntry(*this); +} + +EnumValueTypeEntry::EnumValueTypeEntry(const EnumValueTypeEntry &) = default; + FlagsTypeEntry::FlagsTypeEntry(const QString &name, const QVersionNumber &vr) : TypeEntry(name, FlagsType, vr) { } +/* A typedef entry allows for specifying template specializations in the + * typesystem XML file. */ +TypedefEntry::TypedefEntry(const QString &name, const QString &sourceType, + const QVersionNumber &vr) : + ComplexTypeEntry(name, TypedefType, vr), + m_sourceType(sourceType) +{ +} + +TypeEntry *TypedefEntry::clone() const +{ + return new TypedefEntry(*this); +} + +TypedefEntry::TypedefEntry(const TypedefEntry &) = default; + ContainerTypeEntry::ContainerTypeEntry(const QString &name, Type type, const QVersionNumber &vr) : ComplexTypeEntry(name, ContainerType, vr), @@ -2842,11 +3525,25 @@ SmartPointerTypeEntry::SmartPointerTypeEntry(const QString &name, { } +TypeEntry *SmartPointerTypeEntry::clone() const +{ + return new SmartPointerTypeEntry(*this); +} + +SmartPointerTypeEntry::SmartPointerTypeEntry(const SmartPointerTypeEntry &) = default; + NamespaceTypeEntry::NamespaceTypeEntry(const QString &name, const QVersionNumber &vr) : ComplexTypeEntry(name, NamespaceType, vr) { } +TypeEntry *NamespaceTypeEntry::clone() const +{ + return new NamespaceTypeEntry(*this); +} + +NamespaceTypeEntry::NamespaceTypeEntry(const NamespaceTypeEntry &) = default; + ValueTypeEntry::ValueTypeEntry(const QString &name, const QVersionNumber &vr) : ComplexTypeEntry(name, BasicValueType, vr) { @@ -2862,35 +3559,17 @@ bool ValueTypeEntry::isNativeIdBased() const return true; } -ValueTypeEntry::ValueTypeEntry(const QString &name, Type t, const QVersionNumber &vr) : - ComplexTypeEntry(name, t, vr) +TypeEntry *ValueTypeEntry::clone() const { + return new ValueTypeEntry(*this); } -StringTypeEntry::StringTypeEntry(const QString &name, const QVersionNumber &vr) : - ValueTypeEntry(name, StringType, vr) -{ - setCodeGeneration(GenerateNothing); -} +ValueTypeEntry::ValueTypeEntry(const ValueTypeEntry &) = default; -/* -static void injectCode(ComplexTypeEntry *e, - const char *signature, - const QByteArray &code, - const ArgumentMap &args) +ValueTypeEntry::ValueTypeEntry(const QString &name, Type t, const QVersionNumber &vr) : + ComplexTypeEntry(name, t, vr) { - CodeSnip snip; - snip.language = TypeSystem::NativeCode; - snip.position = CodeSnip::Beginning; - snip.addCode(QString::fromLatin1(code)); - snip.argumentMap = args; - - FunctionModification mod; - mod.signature = QMetaObject::normalizedSignature(signature); - mod.snips << snip; - mod.modifiers = Modification::CodeInjection; } -*/ struct CustomConversion::CustomConversionPrivate { @@ -3042,6 +3721,13 @@ QString InterfaceTypeEntry::qualifiedCppName() const return ComplexTypeEntry::qualifiedCppName().left(len); } +TypeEntry *InterfaceTypeEntry::clone() const +{ + return new InterfaceTypeEntry(*this); +} + +InterfaceTypeEntry::InterfaceTypeEntry(const InterfaceTypeEntry &) = default; + FunctionTypeEntry::FunctionTypeEntry(const QString &name, const QString &signature, const QVersionNumber &vr) : TypeEntry(name, FunctionType, vr) @@ -3049,6 +3735,13 @@ FunctionTypeEntry::FunctionTypeEntry(const QString &name, const QString &signatu addSignature(signature); } +TypeEntry *FunctionTypeEntry::clone() const +{ + return new FunctionTypeEntry(*this); +} + +FunctionTypeEntry::FunctionTypeEntry(const FunctionTypeEntry &) = default; + ObjectTypeEntry::ObjectTypeEntry(const QString &name, const QVersionNumber &vr) : ComplexTypeEntry(name, ObjectType, vr) { @@ -3063,3 +3756,10 @@ bool ObjectTypeEntry::isNativeIdBased() const { return true; } + +TypeEntry *ObjectTypeEntry::clone() const +{ + return new ObjectTypeEntry(*this); +} + +ObjectTypeEntry::ObjectTypeEntry(const ObjectTypeEntry &) = default; diff --git a/sources/shiboken2/ApiExtractor/typesystem.h b/sources/shiboken2/ApiExtractor/typesystem.h index 186c4b24d..721d19f29 100644 --- a/sources/shiboken2/ApiExtractor/typesystem.h +++ b/sources/shiboken2/ApiExtractor/typesystem.h @@ -43,8 +43,8 @@ #include <QtCore/QVersionNumber> //Used to identify the conversion rule to avoid break API -#define TARGET_CONVERSION_RULE_FLAG "0" -#define NATIVE_CONVERSION_RULE_FLAG "1" +extern const char *TARGET_CONVERSION_RULE_FLAG; +extern const char *NATIVE_CONVERSION_RULE_FLAG; class Indentor; @@ -203,12 +203,6 @@ struct ArgumentModification QString replace_value; - // The code to be used to construct a return value when noNullPointers is true and - // the returned value is null. If noNullPointers is true and this string is - // empty, then the base class implementation will be used (or a default construction - // if there is no implementation) - QString nullPointerDefaultValue; - // The text of the new default expression of the argument QString replacedDefaultExpression; @@ -237,6 +231,7 @@ struct ArgumentModification struct Modification { enum Modifiers { + InvalidModifier = 0x0000, Private = 0x0001, Protected = 0x0002, Public = 0x0003, @@ -253,8 +248,7 @@ struct Modification CodeInjection = 0x1000, Rename = 0x2000, Deprecated = 0x4000, - ReplaceExpression = 0x8000, - VirtualSlot = 0x10000 | NonFinal + ReplaceExpression = 0x8000 }; bool isAccessModifier() const @@ -289,10 +283,6 @@ struct Modification { return modifiers & NonFinal; } - bool isVirtualSlot() const - { - return (modifiers & VirtualSlot) == VirtualSlot; - } QString accessModifierString() const; bool isDeprecated() const @@ -325,6 +315,8 @@ struct Modification struct FunctionModification: public Modification { + using AllowThread = TypeSystem::AllowThread; + bool isCodeInjection() const { return modifiers & CodeInjection; @@ -337,14 +329,9 @@ struct FunctionModification: public Modification { return m_thread; } - bool allowThread() const - { - return m_allowThread; - } - void setAllowThread(bool allow) - { - m_allowThread = allow; - } + + AllowThread allowThread() const { return m_allowThread; } + void setAllowThread(AllowThread allow) { m_allowThread = allow; } bool matches(const QString &functionSignature) const { @@ -359,6 +346,9 @@ struct FunctionModification: public Modification void setOriginalSignature(const QString &s) { m_originalSignature = s; } QString originalSignature() const { return m_originalSignature; } + TypeSystem::ExceptionHandling exceptionHandling() const { return m_exceptionHandling; } + void setExceptionHandling(TypeSystem::ExceptionHandling e) { m_exceptionHandling = e; } + QString toString() const; QString association; @@ -371,7 +361,8 @@ private: QString m_originalSignature; QRegularExpression m_signaturePattern; bool m_thread = false; - bool m_allowThread = false; + AllowThread m_allowThread = AllowThread::Unspecified; + TypeSystem::ExceptionHandling m_exceptionHandling = TypeSystem::ExceptionHandling::Unspecified; }; struct FieldModification: public Modification @@ -398,6 +389,7 @@ struct AddedFunction { /// Function access types. enum Access { + InvalidAccess = 0, Protected = 0x1, Public = 0x2 }; @@ -532,7 +524,6 @@ class CustomConversion; class TypeEntry { - Q_DISABLE_COPY(TypeEntry) Q_GADGET public: enum Type { @@ -558,7 +549,8 @@ public: CustomType, TargetLangType, FunctionType, - SmartPointerType + SmartPointerType, + TypedefType }; Q_ENUM(Type) @@ -670,15 +662,6 @@ public: return m_type == EnumValue; } - virtual bool preferredConversion() const - { - return m_preferredConversion; - } - virtual void setPreferredConversion(bool b) - { - m_preferredConversion = b; - } - bool stream() const { return m_stream; @@ -715,6 +698,11 @@ public: && m_codeGeneration != TypeEntry::GenerateNothing; } + int revision() const { return m_revision; } + void setRevision(int r); // see typedatabase.cpp + int sbkIndex() const; + void setSbkIndex(int i) { m_sbkIndex = i; } + virtual QString qualifiedCppName() const { return m_name; @@ -747,10 +735,8 @@ public: } // The package - virtual QString targetLangPackage() const - { - return QString(); - } + QString targetLangPackage() const { return m_targetLangPackage; } + void setTargetLangPackage(const QString &p) { m_targetLangPackage = p; } virtual QString qualifiedTargetLangName() const { @@ -889,13 +875,29 @@ public: bool hasCustomConversion() const; void setCustomConversion(CustomConversion* customConversion); CustomConversion* customConversion() const; + + virtual TypeEntry *clone() const; + + void useAsTypedef(const TypeEntry *source); + +#ifndef QT_NO_DEBUG_STREAM + virtual void formatDebug(QDebug &d) const; +#endif + +protected: + TypeEntry(const TypeEntry &); + private: + TypeEntry &operator=(const TypeEntry &) = delete; + TypeEntry &operator=(TypeEntry &&) = delete; + TypeEntry(TypeEntry &&) = delete; + QString m_name; + QString m_targetLangPackage; Type m_type; uint m_codeGeneration = GenerateAll; CustomFunction m_customConstructor; CustomFunction m_customDestructor; - bool m_preferredConversion = true; CodeSnipList m_codeSnips; DocModificationList m_docModifications; IncludeList m_extraIncludes; @@ -904,24 +906,42 @@ private: QString m_conversionRule; bool m_stream = false; QVersionNumber m_version; + CustomConversion *m_customConversion = nullptr; + int m_revision = 0; + int m_sbkIndex = 0; }; class TypeSystemTypeEntry : public TypeEntry { public: explicit TypeSystemTypeEntry(const QString &name, const QVersionNumber &vr); + + TypeEntry *clone() const override; + +protected: + TypeSystemTypeEntry(const TypeSystemTypeEntry &); }; class VoidTypeEntry : public TypeEntry { public: VoidTypeEntry(); + + TypeEntry *clone() const override; + +protected: + VoidTypeEntry(const VoidTypeEntry &); }; class VarargsTypeEntry : public TypeEntry { public: VarargsTypeEntry(); + + TypeEntry *clone() const override; + +protected: + VarargsTypeEntry(const VarargsTypeEntry &); }; class TemplateArgumentEntry : public TypeEntry @@ -938,6 +958,11 @@ public: m_ordinal = o; } + TypeEntry *clone() const override; + +protected: + TemplateArgumentEntry(const TemplateArgumentEntry &); + private: int m_ordinal = 0; }; @@ -959,6 +984,11 @@ public: QString targetLangName() const override; QString targetLangApiName() const override; + TypeEntry *clone() const override; + +protected: + ArrayTypeEntry(const ArrayTypeEntry &); + private: const TypeEntry *m_nestedType; }; @@ -1019,9 +1049,6 @@ public: */ PrimitiveTypeEntry* basicReferencedTypeEntry() const; - bool preferredConversion() const override; - void setPreferredConversion(bool b) override; - bool preferredTargetLangType() const { return m_preferredTargetLangType; @@ -1031,26 +1058,27 @@ public: m_preferredTargetLangType = b; } - void setTargetLangPackage(const QString& package); - QString targetLangPackage() const override; + TypeEntry *clone() const override; + +protected: + PrimitiveTypeEntry(const PrimitiveTypeEntry &); + private: QString m_targetLangName; QString m_targetLangApiName; QString m_defaultConstructor; - uint m_preferredConversion : 1; uint m_preferredTargetLangType : 1; PrimitiveTypeEntry* m_referencedTypeEntry = nullptr; }; +class EnumValueTypeEntry; + class EnumTypeEntry : public TypeEntry { public: explicit EnumTypeEntry(const QString &nspace, const QString &enumName, const QVersionNumber &vr); - QString targetLangPackage() const override; - void setTargetLangPackage(const QString &package); - QString targetLangName() const override; QString targetLangQualifier() const; QString qualifiedTargetLangName() const override; @@ -1066,30 +1094,8 @@ public: m_qualifier = q; } - bool preferredConversion() const override; - - bool isBoundsChecked() const - { - return m_lowerBound.isEmpty() && m_upperBound.isEmpty(); - } - - QString upperBound() const - { - return m_upperBound; - } - void setUpperBound(const QString &bound) - { - m_upperBound = bound; - } - - QString lowerBound() const - { - return m_lowerBound; - } - void setLowerBound(const QString &bound) - { - m_lowerBound = bound; - } + const EnumValueTypeEntry *nullValue() const { return m_nullValue; } + void setNullValue(const EnumValueTypeEntry *n) { m_nullValue = n; } void setFlags(FlagsTypeEntry *flags) { @@ -1100,15 +1106,6 @@ public: return m_flags; } - bool isExtensible() const - { - return m_extensible; - } - void setExtensible(bool is) - { - m_extensible = is; - } - bool isEnumValueRejected(const QString &name) const { return m_rejectedEnums.contains(name); @@ -1122,33 +1119,27 @@ public: return m_rejectedEnums; } - bool forceInteger() const - { - return m_forceInteger; - } - void setForceInteger(bool force) - { - m_forceInteger = force; - } + TypeEntry *clone() const override; +#ifndef QT_NO_DEBUG_STREAM + void formatDebug(QDebug &d) const override; +#endif +protected: + EnumTypeEntry(const EnumTypeEntry &); private: QString m_packageName; QString m_qualifier; QString m_targetLangName; - - QString m_lowerBound; - QString m_upperBound; + const EnumValueTypeEntry *m_nullValue = nullptr; QStringList m_rejectedEnums; FlagsTypeEntry *m_flags = nullptr; - - bool m_extensible = false; - bool m_forceInteger = false; }; // EnumValueTypeEntry is used for resolving integer type templates -// like array<EnumValue>. +// like array<EnumValue>. Note: Dummy entries for integer values will +// be created for non-type template parameters, where m_enclosingEnum==nullptr. class EnumValueTypeEntry : public TypeEntry { public: @@ -1156,6 +1147,12 @@ public: QString value() const { return m_value; } const EnumTypeEntry* enclosingEnum() const { return m_enclosingEnum; } + + TypeEntry *clone() const override; + +protected: + EnumValueTypeEntry(const EnumValueTypeEntry &); + private: QString m_value; const EnumTypeEntry* m_enclosingEnum; @@ -1169,7 +1166,6 @@ public: QString qualifiedTargetLangName() const override; QString targetLangName() const override; QString targetLangApiName() const override; - bool preferredConversion() const override; QString originalName() const { @@ -1189,11 +1185,6 @@ public: m_targetLangName = name; } - bool forceInteger() const - { - return m_enum->forceInteger(); - } - EnumTypeEntry *originator() const { return m_enum; @@ -1203,7 +1194,10 @@ public: m_enum = e; } - QString targetLangPackage() const override; + TypeEntry *clone() const override; + +protected: + FlagsTypeEntry(const FlagsTypeEntry &); private: QString m_originalName; @@ -1216,8 +1210,6 @@ class ComplexTypeEntry : public TypeEntry { public: enum TypeFlag { - ForceAbstract = 0x1, - DeleteInMainThread = 0x2, Deprecated = 0x4 }; typedef QFlags<TypeFlag> TypeFlags; @@ -1288,12 +1280,6 @@ public: return m_fieldMods; } - QString targetLangPackage() const override; - void setTargetLangPackage(const QString &package) - { - m_package = package; - } - bool isQObject() const { return m_qobject; @@ -1336,15 +1322,6 @@ public: return m_polymorphicIdValue; } - void setHeldType(const QString &value) - { - m_heldTypeValue = value; - } - QString heldTypeValue() const - { - return m_heldTypeValue; - } - QString targetType() const { return m_targetType; @@ -1369,6 +1346,9 @@ public: m_genericClass = isGeneric; } + bool deleteInMainThread() const { return m_deleteInMainThread; } + void setDeleteInMainThread(bool d) { m_deleteInMainThread = d; } + CopyableFlag copyable() const { return m_copyableFlag; @@ -1397,15 +1377,28 @@ public: return m_baseContainerType; } + TypeSystem::ExceptionHandling exceptionHandling() const { return m_exceptionHandling; } + void setExceptionHandling(TypeSystem::ExceptionHandling e) { m_exceptionHandling = e; } + QString defaultConstructor() const; void setDefaultConstructor(const QString& defaultConstructor); bool hasDefaultConstructor() const; + TypeEntry *clone() const override; + + void useAsTypedef(const ComplexTypeEntry *source); + +#ifndef QT_NO_DEBUG_STREAM + void formatDebug(QDebug &d) const override; +#endif +protected: + ComplexTypeEntry(const ComplexTypeEntry &); + private: AddedFunctionList m_addedFunctions; FunctionModificationList m_functionMods; FieldModificationList m_fieldMods; - QString m_package; + QString m_defaultConstructor; QString m_defaultSuperclass; QString m_qualifiedCppName; QString m_targetLangName; @@ -1413,9 +1406,9 @@ private: uint m_qobject : 1; uint m_polymorphicBase : 1; uint m_genericClass : 1; + uint m_deleteInMainThread : 1; QString m_polymorphicIdValue; - QString m_heldTypeValue; QString m_lookupName; QString m_targetType; TypeFlags m_typeFlags; @@ -1423,6 +1416,38 @@ private: QString m_hashFunction; const ComplexTypeEntry* m_baseContainerType = nullptr; + // For class functions + TypeSystem::ExceptionHandling m_exceptionHandling = TypeSystem::ExceptionHandling::Unspecified; +}; + +class TypedefEntry : public ComplexTypeEntry +{ +public: + explicit TypedefEntry(const QString &name, + const QString &sourceType, + const QVersionNumber &vr); + + QString sourceType() const { return m_sourceType; } + void setSourceType(const QString &s) { m_sourceType =s; } + + TypeEntry *clone() const override; + + ComplexTypeEntry *source() const { return m_source; } + void setSource(ComplexTypeEntry *source) { m_source = source; } + + ComplexTypeEntry *target() const { return m_target; } + void setTarget(ComplexTypeEntry *target) { m_target = target; } + +#ifndef QT_NO_DEBUG_STREAM + virtual void formatDebug(QDebug &d) const override; +#endif +protected: + TypedefEntry(const TypedefEntry &); + +private: + QString m_sourceType; + ComplexTypeEntry *m_source = nullptr; + ComplexTypeEntry *m_target = nullptr; }; class ContainerTypeEntry : public ComplexTypeEntry @@ -1455,28 +1480,15 @@ public: QString typeName() const; QString targetLangName() const override; - QString targetLangPackage() const override; QString qualifiedCppName() const override; - static Type containerTypeFromString(QString typeName) - { - static QHash<QString, Type> m_stringToContainerType; - if (m_stringToContainerType.isEmpty()) { - m_stringToContainerType.insert(QLatin1String("list"), ListContainer); - m_stringToContainerType.insert(QLatin1String("string-list"), StringListContainer); - m_stringToContainerType.insert(QLatin1String("linked-list"), LinkedListContainer); - m_stringToContainerType.insert(QLatin1String("vector"), VectorContainer); - m_stringToContainerType.insert(QLatin1String("stack"), StackContainer); - m_stringToContainerType.insert(QLatin1String("queue"), QueueContainer); - m_stringToContainerType.insert(QLatin1String("set"), SetContainer); - m_stringToContainerType.insert(QLatin1String("map"), MapContainer); - m_stringToContainerType.insert(QLatin1String("multi-map"), MultiMapContainer); - m_stringToContainerType.insert(QLatin1String("hash"), HashContainer); - m_stringToContainerType.insert(QLatin1String("multi-hash"), MultiHashContainer); - m_stringToContainerType.insert(QLatin1String("pair"), PairContainer); - } - return m_stringToContainerType.value(typeName, NoContainer); - } + TypeEntry *clone() const override; + +#ifndef QT_NO_DEBUG_STREAM + void formatDebug(QDebug &d) const override; +#endif +protected: + ContainerTypeEntry(const ContainerTypeEntry &); private: Type m_type; @@ -1501,6 +1513,11 @@ public: return m_refCountMethodName; } + TypeEntry *clone() const override; + +protected: + SmartPointerTypeEntry(const SmartPointerTypeEntry &); + private: QString m_getterName; QString m_smartPointerType; @@ -1511,6 +1528,11 @@ class NamespaceTypeEntry : public ComplexTypeEntry { public: explicit NamespaceTypeEntry(const QString &name, const QVersionNumber &vr); + + TypeEntry *clone() const override; + +protected: + NamespaceTypeEntry(const NamespaceTypeEntry &); }; @@ -1523,48 +1545,13 @@ public: bool isNativeIdBased() const override; + TypeEntry *clone() const override; + protected: explicit ValueTypeEntry(const QString &name, Type t, const QVersionNumber &vr); + ValueTypeEntry(const ValueTypeEntry &); }; - -class StringTypeEntry : public ValueTypeEntry -{ -public: - explicit StringTypeEntry(const QString &name, const QVersionNumber &vr); - - QString targetLangApiName() const override; - QString targetLangName() const override; - QString targetLangPackage() const override; - - bool isNativeIdBased() const override; -}; - -class CharTypeEntry : public ValueTypeEntry -{ -public: - explicit CharTypeEntry(const QString &name, const QVersionNumber &vr); - - QString targetLangApiName() const override; - QString targetLangName() const override; - QString targetLangPackage() const override; - - bool isNativeIdBased() const override; -}; - -class VariantTypeEntry: public ValueTypeEntry -{ -public: - explicit VariantTypeEntry(const QString &name, const QVersionNumber &vr); - - QString targetLangApiName() const override; - QString targetLangName() const override; - QString targetLangPackage() const override; - - bool isNativeIdBased() const override; -}; - - class InterfaceTypeEntry : public ComplexTypeEntry { public: @@ -1587,6 +1574,11 @@ public: bool isNativeIdBased() const override; QString qualifiedCppName() const override; + TypeEntry *clone() const override; + +protected: + InterfaceTypeEntry(const InterfaceTypeEntry &); + private: ObjectTypeEntry *m_origin; }; @@ -1611,6 +1603,12 @@ public: { return m_signatures.contains(signature); } + + TypeEntry *clone() const override; + +protected: + FunctionTypeEntry(const FunctionTypeEntry &); + private: QStringList m_signatures; }; @@ -1628,6 +1626,11 @@ public: bool isNativeIdBased() const override; + TypeEntry *clone() const override; + +protected: + ObjectTypeEntry(const ObjectTypeEntry &); + private: InterfaceTypeEntry *m_interface = nullptr; }; @@ -1641,7 +1644,8 @@ struct TypeRejection Field, // Match className and field name Enum, // Match className and enum name ArgumentType, // Match className and argument type - ReturnType // Match className and return type + ReturnType, // Match className and return type + Invalid }; QRegularExpression className; diff --git a/sources/shiboken2/ApiExtractor/typesystem_enums.h b/sources/shiboken2/ApiExtractor/typesystem_enums.h index 6bfc94368..df83429d0 100644 --- a/sources/shiboken2/ApiExtractor/typesystem_enums.h +++ b/sources/shiboken2/ApiExtractor/typesystem_enums.h @@ -55,6 +55,13 @@ enum Language { TargetLangAndNativeCode = TargetLangCode | NativeCode }; +enum class AllowThread { + Allow, + Disallow, + Auto, + Unspecified +}; + enum Ownership { InvalidOwnership, DefaultOwnership, @@ -71,14 +78,24 @@ enum CodeSnipPosition { CodeSnipPositionPrototypeInitialization, CodeSnipPositionConstructorInitialization, CodeSnipPositionConstructor, - CodeSnipPositionAny + CodeSnipPositionAny, + CodeSnipPositionInvalid }; enum DocModificationMode { DocModificationAppend, DocModificationPrepend, DocModificationReplace, - DocModificationXPathReplace + DocModificationXPathReplace, + DocModificationInvalid +}; + +enum class ExceptionHandling { + Unspecified, + Off, + AutoDefaultToOff, + AutoDefaultToOn, + On }; } // namespace TypeSystem diff --git a/sources/shiboken2/ApiExtractor/typesystem_p.h b/sources/shiboken2/ApiExtractor/typesystem_p.h index 882cf3fab..e36df5151 100644 --- a/sources/shiboken2/ApiExtractor/typesystem_p.h +++ b/sources/shiboken2/ApiExtractor/typesystem_p.h @@ -55,6 +55,7 @@ class StackElement FunctionTypeEntry = 0xb, CustomTypeEntry = 0xc, SmartPointerTypeEntry = 0xd, + TypedefTypeEntry = 0xe, TypeEntryMask = 0xf, // Documentation tags @@ -140,37 +141,114 @@ public: bool parse(QXmlStreamReader &reader); + QString errorString() const { return m_error; } + private: - bool startElement(const QStringRef& localName, const QXmlStreamAttributes& atts); - bool handleSmartPointerEntry(StackElement *element, - QHash<QString, QString> &attributes, - const QString &name, - const QVersionNumber &since); + bool startElement(const QXmlStreamReader &reader); + SmartPointerTypeEntry *parseSmartPointerEntry(const QXmlStreamReader &, + const QString &name, + const QVersionNumber &since, + QXmlStreamAttributes *attributes); bool endElement(const QStringRef& localName); template <class String> // QString/QStringRef bool characters(const String &ch); - void fetchAttributeValues(const QString &name, const QXmlStreamAttributes &atts, - QHash<QString, QString> *acceptedAttributes); bool importFileElement(const QXmlStreamAttributes &atts); - void addFlags(const QString &name, QString flagName, - const QHash<QString, QString> &attributes, - const QVersionNumber &since); + + void applyCommonAttributes(TypeEntry *type, QXmlStreamAttributes *attributes) const; + PrimitiveTypeEntry * + parsePrimitiveTypeEntry(const QXmlStreamReader &, const QString &name, + const QVersionNumber &since, QXmlStreamAttributes *); + ContainerTypeEntry * + parseContainerTypeEntry(const QXmlStreamReader &, const QString &name, + const QVersionNumber &since, QXmlStreamAttributes *); + EnumTypeEntry * + parseEnumTypeEntry(const QXmlStreamReader &, const QString &name, + const QVersionNumber &since, QXmlStreamAttributes *); + FlagsTypeEntry * + parseFlagsEntry(const QXmlStreamReader &, EnumTypeEntry *enumEntry, + const QString &name, QString flagName, + const QVersionNumber &since, QXmlStreamAttributes *); + ObjectTypeEntry * + parseInterfaceTypeEntry(const QXmlStreamReader &, const QString &name, + const QVersionNumber &since, QXmlStreamAttributes *); + ValueTypeEntry * + parseValueTypeEntry(const QXmlStreamReader &, const QString &name, + const QVersionNumber &since, QXmlStreamAttributes *); + FunctionTypeEntry * + parseFunctionTypeEntry(const QXmlStreamReader &, const QString &name, + const QVersionNumber &since, QXmlStreamAttributes *); + TypedefEntry * + parseTypedefEntry(const QXmlStreamReader &, const QString &name, + const QVersionNumber &since, QXmlStreamAttributes *); + void applyComplexTypeAttributes(const QXmlStreamReader &, ComplexTypeEntry *ctype, + QXmlStreamAttributes *) const; + bool parseRenameFunction(const QXmlStreamReader &, QString *name, + QXmlStreamAttributes *); + bool parseInjectDocumentation(const QXmlStreamReader &, QXmlStreamAttributes *); + bool parseModifyDocumentation(const QXmlStreamReader &, QXmlStreamAttributes *); + TypeSystemTypeEntry * + parseRootElement(const QXmlStreamReader &, const QVersionNumber &since, + QXmlStreamAttributes *); + bool loadTypesystem(const QXmlStreamReader &, QXmlStreamAttributes *); + bool parseRejectEnumValue(const QXmlStreamReader &, QXmlStreamAttributes *); + bool parseReplaceArgumentType(const QXmlStreamReader &, const StackElement &topElement, + QXmlStreamAttributes *); + bool parseCustomConversion(const QXmlStreamReader &, const StackElement &topElement, + QXmlStreamAttributes *); + bool parseAddConversion(const QXmlStreamReader &, const StackElement &topElement, + QXmlStreamAttributes *); + bool parseModifyArgument(const QXmlStreamReader &, const StackElement &topElement, + QXmlStreamAttributes *attributes); + bool parseNoNullPointer(const QXmlStreamReader &, const StackElement &topElement, + QXmlStreamAttributes *attributes); + bool parseDefineOwnership(const QXmlStreamReader &, const StackElement &topElement, + QXmlStreamAttributes *); + bool parseArgumentMap(const QXmlStreamReader &, const StackElement &topElement, + QXmlStreamAttributes *); + bool parseRemoval(const QXmlStreamReader &, const StackElement &topElement, + QXmlStreamAttributes *); + bool parseRename(const QXmlStreamReader &, StackElement::ElementType type, + const StackElement &topElement, QXmlStreamAttributes *); + bool parseModifyField(const QXmlStreamReader &, QXmlStreamAttributes *); + bool parseAddFunction(const QXmlStreamReader &, const StackElement &topElement, + QXmlStreamAttributes *); + bool parseModifyFunction(const QXmlStreamReader &, const StackElement &topElement, + QXmlStreamAttributes *); + bool parseReplaceDefaultExpression(const QXmlStreamReader &, + const StackElement &topElement, QXmlStreamAttributes *); + CustomFunction * + parseCustomMetaConstructor(const QXmlStreamReader &, + StackElement::ElementType type, + const StackElement &topElement, QXmlStreamAttributes *); + bool parseReferenceCount(const QXmlStreamReader &, const StackElement &topElement, + QXmlStreamAttributes *); + bool parseParentOwner(const QXmlStreamReader &, const StackElement &topElement, + QXmlStreamAttributes *); + bool parseInjectCode(const QXmlStreamReader &, const StackElement &topElement, + StackElement* element, QXmlStreamAttributes *); + bool parseInclude(const QXmlStreamReader &, const StackElement &topElement, + TypeEntry *entry, QXmlStreamAttributes *); + TemplateInstance + *parseTemplateInstanceEnum(const QXmlStreamReader &, const StackElement &topElement, + QXmlStreamAttributes *); + bool parseReplace(const QXmlStreamReader &, const StackElement &topElement, + StackElement *element, QXmlStreamAttributes *); TypeDatabase* m_database; - StackElement* m_current; - StackElement* m_currentDroppedEntry; - int m_currentDroppedEntryDepth; - int m_ignoreDepth; + StackElement* m_current = nullptr; + StackElement* m_currentDroppedEntry = nullptr; + int m_currentDroppedEntryDepth = 0; + int m_ignoreDepth = 0; QString m_defaultPackage; QString m_defaultSuperclass; + TypeSystem::ExceptionHandling m_exceptionHandling = TypeSystem::ExceptionHandling::Unspecified; QString m_error; - TypeEntry::CodeGeneration m_generate; + const TypeEntry::CodeGeneration m_generate; - EnumTypeEntry* m_currentEnum; + EnumTypeEntry* m_currentEnum = nullptr; QStack<StackElementContext*> m_contextStack; - QHash<QString, StackElement::ElementType> tagNames; QString m_currentSignature; QString m_currentPath; }; diff --git a/sources/shiboken2/CMakeLists.txt b/sources/shiboken2/CMakeLists.txt index 1af84fca1..12a9b8773 100644 --- a/sources/shiboken2/CMakeLists.txt +++ b/sources/shiboken2/CMakeLists.txt @@ -5,8 +5,9 @@ include(CheckIncludeFileCXX) cmake_minimum_required(VERSION 3.1) cmake_policy(VERSION 3.1) -set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules/ +set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/../cmake_helpers/ ${CMAKE_MODULE_PATH}) +include(helpers) find_package(Qt5 5.7 REQUIRED COMPONENTS Core Xml XmlPatterns) @@ -158,6 +159,8 @@ list(GET SHIBOKEN_VERSION_OUTPUT 4 shiboken_PRE_RELEASE_VERSION) set(shiboken2_VERSION "${shiboken_MAJOR_VERSION}.${shiboken_MINOR_VERSION}.${shiboken_MICRO_VERSION}") set(shiboken2_library_so_version "${shiboken_MAJOR_VERSION}.${shiboken_MINOR_VERSION}") +compute_config_py_values(shiboken2_VERSION) + ## For debugging the PYTHON* variables message("PYTHONLIBS_FOUND: " ${PYTHONLIBS_FOUND}) message("PYTHON_LIBRARIES: " ${PYTHON_LIBRARIES}) diff --git a/sources/shiboken2/doc/commandlineoptions.rst b/sources/shiboken2/doc/commandlineoptions.rst index d373561cd..c335fab75 100644 --- a/sources/shiboken2/doc/commandlineoptions.rst +++ b/sources/shiboken2/doc/commandlineoptions.rst @@ -37,16 +37,22 @@ Options Enable heuristics to detect parent relationship on return values. For more info, check :ref:`return-value-heuristics`. +.. _avoid-protected-hack: + +``--avoid-protected-hack`` + Avoid the use of the '#define protected public' hack. + +.. _use-isnull-as-nb_nonzero: + +``--use-isnull-as-nb_nonzero`` + If a class have an isNull() const method, it will be used to + compute the value of boolean casts + .. _api-version: ``--api-version=<version>`` Specify the supported api version used to generate the bindings. -.. _debug-level: - -``--debug-level=[sparse|medium|full]`` - Set the debug level. - .. _documentation-only: ``--documentation-only`` @@ -63,16 +69,52 @@ Options ``--generation-set`` Generator set to be used (e.g. qtdoc). -.. _help: +.. _diff: -``--help`` - Display this help and exit. +``--diff`` + Print a diff of wrapper files. + +.. _dryrun: + +``--dryrun`` + Dry run, do not generate wrapper files. + +.. _--project-file: + +``--project-file=<file>`` + Text file containing a description of the binding project. + Replaces and overrides command line arguments. .. _include-paths: -``--include-paths=<path>[:<path>:...]`` +``-I<path>, --include-paths=<path>[:<path>:...]`` Include paths used by the C++ parser. +... _system-include-paths: + +``-isystem<path>, --system-include-paths=<path>[:<path>:...]`` + System include paths used by the C++ parser + +.. _framework-include-paths: + +``-F<path>, --framework-include-paths=<path>[:<path>:...]`` + Framework include paths used by the C++ parser + +.. _language-level: + +``--language-level=, -std=<level>`` + C++ Language level (c++11..c++17, default=c++14) + +.. _typesystem-paths: + +``-T<path>, --typesystem-paths=<path>[:<path>:...]`` + Paths used when searching for type system files. + +.. _output-directory: + +``--output-directory=[dir]`` + The directory where the generated files will be written. + .. _license-file=[license-file]: ``--license-file=[license-file]`` @@ -83,23 +125,57 @@ Options ``--no-suppress-warnings`` Show all warnings. -.. _output-directory: - -``--output-directory=[dir]`` - The directory where the generated files will be written. - .. _silent: ``--silent`` Avoid printing any message. -.. _typesystem-paths: +.. _debug-level: -``--typesystem-paths=<path>[:<path>:...]`` - Paths used when searching for type system files. +``--debug-level=[sparse|medium|full]`` + Set the debug level. + +.. _help: + +``--help`` + Display this help and exit. .. _version: ``--version`` Output version information and exit. +QtDocGenerator Options +---------------------- + +.. _doc-parser: + +``--doc-parser=<parser>`` + The documentation parser used to interpret the documentation + input files (qdoc|doxygen). + +.. _documentation-code-snippets-dir: + +``--documentation-code-snippets-dir=<dir>`` + Directory used to search code snippets used by the documentation. + +.. _documentation-data-dir: + +``--documentation-data-dir=<dir>`` + Directory with XML files generated by documentation tool. + +.. _documentation-extra-sections-dir=<dir>: + +``--documentation-extra-sections-dir=<dir>`` + Directory used to search for extra documentation sections. + +.. _library-source-dir: + +``--library-source-dir=<dir>`` + Directory where library source code is located. + +.. _additional-documentation: + +``--additional-documentation=<file>`` + List of additional XML files to be converted to .rst files + (for example, tutorials). diff --git a/sources/shiboken2/generator/CMakeLists.txt b/sources/shiboken2/generator/CMakeLists.txt index 032118666..fb8058b2d 100644 --- a/sources/shiboken2/generator/CMakeLists.txt +++ b/sources/shiboken2/generator/CMakeLists.txt @@ -38,3 +38,33 @@ target_link_libraries(shiboken2 configure_file(shibokenconfig.h.in "${CMAKE_CURRENT_BINARY_DIR}/shibokenconfig.h" @ONLY) install(TARGETS shiboken2 DESTINATION bin) + +set(shiboken_generator_package_name "shiboken2_generator") + +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/_config.py.in" + "${CMAKE_CURRENT_BINARY_DIR}/_config.py" @ONLY) +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/_config.py" + DESTINATION "${PYTHON_SITE_PACKAGES}/${shiboken_generator_package_name}") + +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/__init__.py.in" + "${CMAKE_CURRENT_BINARY_DIR}/__init__.py" @ONLY) +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/__init__.py" + DESTINATION "${PYTHON_SITE_PACKAGES}/${shiboken_generator_package_name}") + +# shiboken2 setuptools entry point +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/../shiboken_tool.py + DESTINATION bin + PERMISSIONS + OWNER_EXECUTE OWNER_WRITE OWNER_READ + GROUP_EXECUTE GROUP_READ + WORLD_EXECUTE WORLD_READ) + +# Use absolute path instead of relative path, to avoid ninja build errors due to +# duplicate file dependency inconsistency. +set(shiboken_version_relative_path "${CMAKE_CURRENT_SOURCE_DIR}/../shiboken_version.py") +get_filename_component(shiboken_version_path ${shiboken_version_relative_path} ABSOLUTE) +configure_file("${shiboken_version_path}" + "${CMAKE_CURRENT_BINARY_DIR}/_git_shiboken_generator_version.py" @ONLY) + +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/_git_shiboken_generator_version.py" + DESTINATION "${PYTHON_SITE_PACKAGES}/${shiboken_generator_package_name}") diff --git a/sources/shiboken2/generator/__init__.py.in b/sources/shiboken2/generator/__init__.py.in new file mode 100644 index 000000000..4be6a833b --- /dev/null +++ b/sources/shiboken2/generator/__init__.py.in @@ -0,0 +1,2 @@ +__version__ = "@FINAL_PACKAGE_VERSION@" +__version_info__ = (@shiboken_MAJOR_VERSION@, @shiboken_MINOR_VERSION@, @shiboken_MICRO_VERSION@, "@shiboken_PRE_RELEASE_VERSION_TYPE@", "@shiboken_PRE_RELEASE_VERSION@") diff --git a/sources/shiboken2/generator/_config.py.in b/sources/shiboken2/generator/_config.py.in new file mode 100644 index 000000000..985735fa4 --- /dev/null +++ b/sources/shiboken2/generator/_config.py.in @@ -0,0 +1,9 @@ +version = "@FINAL_PACKAGE_VERSION@" +version_info = (@shiboken_MAJOR_VERSION@, @shiboken_MINOR_VERSION@, @shiboken_MICRO_VERSION@, "@shiboken_PRE_RELEASE_VERSION_TYPE@", "@shiboken_PRE_RELEASE_VERSION@") + +@PACKAGE_BUILD_DATE@ +@PACKAGE_BUILD_COMMIT_DATE@ +@PACKAGE_BUILD_COMMIT_HASH@ +@PACKAGE_BUILD_COMMIT_HASH_DESCRIBED@ +@PACKAGE_SETUP_PY_PACKAGE_TIMESTAMP_ASSIGNMENT@ +@PACKAGE_SETUP_PY_PACKAGE_VERSION_ASSIGNMENT@ diff --git a/sources/shiboken2/generator/generator.cpp b/sources/shiboken2/generator/generator.cpp index 1e2f03932..8b37b44e0 100644 --- a/sources/shiboken2/generator/generator.cpp +++ b/sources/shiboken2/generator/generator.cpp @@ -28,6 +28,7 @@ #include "generator.h" #include "abstractmetalang.h" +#include "messages.h" #include "reporthandler.h" #include "fileout.h" #include "apiextractor.h" @@ -40,8 +41,113 @@ #include <QDebug> #include <typedatabase.h> -struct Generator::GeneratorPrivate { - const ApiExtractor* apiextractor; +/** + * DefaultValue is used for storing default values of types for which code is + * generated in different contexts: + * + * Context | Example: "Class *" | Example: "Class" with default Constructor + * --------------------+-------------------------------+------------------------------------------ + * Variable | var{nullptr}; | var; + * initializations | | + * --------------------+-------------------------------+------------------------------------------ + * Return values | return nullptr; | return {} + * --------------------+-------------------------------+------------------------------------------ + * constructor | static_cast<Class *>(nullptr) | Class() + * arguments lists | | + * (recursive, precise | | + * matching). | | + */ + +DefaultValue::DefaultValue(Type t, QString value) : + m_type(t), m_value(std::move(value)) +{ +} + +DefaultValue::DefaultValue(QString customValue) : + m_type(Custom), m_value(std::move(customValue)) +{ +} + +QString DefaultValue::returnValue() const +{ + switch (m_type) { + case DefaultValue::Error: + return QLatin1String("#error"); + case DefaultValue::Boolean: + return QLatin1String("false"); + case DefaultValue::CppScalar: + return QLatin1String("0"); + case DefaultValue::Custom: + case DefaultValue::Enum: + return m_value; + case DefaultValue::Pointer: + return QLatin1String("nullptr"); + case DefaultValue::Void: + return QString(); + case DefaultValue::DefaultConstructorWithDefaultValues: + return m_value + QLatin1String("()"); + case DefaultValue::DefaultConstructor: + break; + } + return QLatin1String("{}"); +} + +QString DefaultValue::initialization() const +{ + switch (m_type) { + case DefaultValue::Error: + return QLatin1String("#error"); + case DefaultValue::Boolean: + return QLatin1String("{false}"); + case DefaultValue::CppScalar: + return QLatin1String("{0}"); + case DefaultValue::Custom: + return QLatin1String(" = ") + m_value; + case DefaultValue::Enum: + return QLatin1Char('{') + m_value + QLatin1Char('}'); + case DefaultValue::Pointer: + return QLatin1String("{nullptr}"); + case DefaultValue::Void: + Q_ASSERT(false); + break; + case DefaultValue::DefaultConstructor: + case DefaultValue::DefaultConstructorWithDefaultValues: + break; + } + return QString(); +} + +QString DefaultValue::constructorParameter() const +{ + switch (m_type) { + case DefaultValue::Error: + return QLatin1String("#error"); + case DefaultValue::Boolean: + return QLatin1String("false"); + case DefaultValue::CppScalar: + return m_value + QLatin1String("(0)"); + case DefaultValue::Custom: + case DefaultValue::Enum: + return m_value; + case DefaultValue::Pointer: + // Be precise here to be able to differentiate between constructors + // taking different pointer types, cf + // QTreeWidgetItemIterator(QTreeWidget *) and + // QTreeWidgetItemIterator(QTreeWidgetItemIterator *). + return QLatin1String("static_cast<") + m_value + QLatin1String("*>(nullptr)"); + case DefaultValue::Void: + Q_ASSERT(false); + break; + case DefaultValue::DefaultConstructor: + case DefaultValue::DefaultConstructorWithDefaultValues: + break; + } + return m_value + QLatin1String("()"); +} + +struct Generator::GeneratorPrivate +{ + const ApiExtractor* apiextractor = nullptr; QString outDir; // License comment QString licenseComment; @@ -62,20 +168,17 @@ Generator::~Generator() delete m_d; } -bool Generator::setup(const ApiExtractor& extractor, const QMap< QString, QString > args) +bool Generator::setup(const ApiExtractor& extractor) { m_d->apiextractor = &extractor; - TypeEntryHash allEntries = TypeDatabase::instance()->allEntries(); + const auto &allEntries = TypeDatabase::instance()->entries(); TypeEntry* entryFound = 0; - for (TypeEntryHash::const_iterator it = allEntries.cbegin(), end = allEntries.cend(); it != end; ++it) { - for (TypeEntry *entry : it.value()) { - if (entry->type() == TypeEntry::TypeSystemType && entry->generateCode()) { - entryFound = entry; - break; - } - } - if (entryFound) + for (auto it = allEntries.cbegin(), end = allEntries.cend(); it != end; ++it) { + TypeEntry *entry = it.value(); + if (entry->type() == TypeEntry::TypeSystemType && entry->generateCode()) { + entryFound = entry; break; + } } if (entryFound) m_d->packageName = entryFound->name(); @@ -84,7 +187,7 @@ bool Generator::setup(const ApiExtractor& extractor, const QMap< QString, QStrin collectInstantiatedContainersAndSmartPointers(); - return doSetup(args); + return doSetup(); } QString Generator::getSimplifiedContainerTypeName(const AbstractMetaType* type) @@ -197,6 +300,11 @@ Generator::OptionDescriptions Generator::options() const return OptionDescriptions(); } +bool Generator::handleOption(const QString & /* key */, const QString & /* value */) +{ + return false; +} + AbstractMetaClassList Generator::classes() const { return m_d->apiextractor->classes(); @@ -227,24 +335,14 @@ ContainerTypeEntryList Generator::containerTypes() const return m_d->apiextractor->containerTypes(); } -const AbstractMetaEnum* Generator::findAbstractMetaEnum(const EnumTypeEntry* typeEntry) const -{ - return m_d->apiextractor->findAbstractMetaEnum(typeEntry); -} - const AbstractMetaEnum* Generator::findAbstractMetaEnum(const TypeEntry* typeEntry) const { return m_d->apiextractor->findAbstractMetaEnum(typeEntry); } -const AbstractMetaEnum* Generator::findAbstractMetaEnum(const FlagsTypeEntry* typeEntry) const -{ - return m_d->apiextractor->findAbstractMetaEnum(typeEntry); -} - const AbstractMetaEnum* Generator::findAbstractMetaEnum(const AbstractMetaType* metaType) const { - return m_d->apiextractor->findAbstractMetaEnum(metaType); + return m_d->apiextractor->findAbstractMetaEnum(metaType->typeEntry()); } QString Generator::licenseComment() const @@ -278,21 +376,6 @@ void Generator::setOutputDirectory(const QString &outDir) m_d->outDir = outDir; } -inline void touchFile(const QString &filePath) -{ - QFile toucher(filePath); - qint64 size = toucher.size(); - if (!toucher.open(QIODevice::ReadWrite)) { - qCWarning(lcShiboken).noquote().nospace() - << QStringLiteral("Failed to touch file '%1'") - .arg(QDir::toNativeSeparators(filePath)); - return; - } - toucher.resize(size+1); - toucher.resize(size); - toucher.close(); -} - bool Generator::generateFileForContext(GeneratorContext &context) { AbstractMetaClass *cls = context.metaClass(); @@ -312,20 +395,7 @@ bool Generator::generateFileForContext(GeneratorContext &context) generateClass(fileOut.stream, context); - FileOut::State state = fileOut.done(); - switch (state) { - case FileOut::Failure: - return false; - case FileOut::Unchanged: - // Even if contents is unchanged, the last file modification time should be updated, - // so that the build system can rely on the fact the generated file is up-to-date. - touchFile(filePath); - break; - case FileOut::Success: - break; - } - - return true; + return fileOut.done() != FileOut::Failure; } QString Generator::getFileNameBaseForSmartPointer(const AbstractMetaType *smartPointerType, @@ -369,9 +439,9 @@ bool Generator::shouldGenerate(const AbstractMetaClass* metaClass) const return shouldGenerateTypeEntry(metaClass->typeEntry()); } -void verifyDirectoryFor(const QFile &file) +void verifyDirectoryFor(const QString &file) { - QDir dir = QFileInfo(file).dir(); + QDir dir = QFileInfo(file).absoluteDir(); if (!dir.exists()) { if (!dir.mkpath(dir.absolutePath())) { qCWarning(lcShiboken).noquote().nospace() @@ -463,7 +533,7 @@ AbstractMetaFunctionList Generator::implicitConversions(const AbstractMetaType* bool Generator::isObjectType(const TypeEntry* type) { if (type->isComplex()) - return Generator::isObjectType((const ComplexTypeEntry*)type); + return Generator::isObjectType(static_cast<const ComplexTypeEntry *>(type)); return type->isObject(); } bool Generator::isObjectType(const ComplexTypeEntry* type) @@ -557,184 +627,178 @@ QString Generator::getFullTypeNameWithoutModifiers(const AbstractMetaType* type) return QLatin1String("::") + typeName; } -QString Generator::minimalConstructor(const AbstractMetaType* type) const +DefaultValue Generator::minimalConstructor(const AbstractMetaType* type) const { if (!type || (type->referenceType() == LValueReference && Generator::isObjectType(type))) - return QString(); + return DefaultValue(DefaultValue::Error); if (type->isContainer()) { QString ctor = type->cppSignature(); - if (ctor.endsWith(QLatin1Char('*'))) - return QLatin1String("0"); + if (ctor.endsWith(QLatin1Char('*'))) { + ctor.chop(1); + return DefaultValue(DefaultValue::Pointer, ctor.trimmed()); + } if (ctor.startsWith(QLatin1String("const "))) ctor.remove(0, sizeof("const ") / sizeof(char) - 1); if (ctor.endsWith(QLatin1Char('&'))) { ctor.chop(1); ctor = ctor.trimmed(); } - return QLatin1String("::") + ctor + QLatin1String("()"); + return DefaultValue(DefaultValue::DefaultConstructor, QLatin1String("::") + ctor); } if (type->isNativePointer()) - return QLatin1String("static_cast<") + type->typeEntry()->qualifiedCppName() + QLatin1String(" *>(0)"); + return DefaultValue(DefaultValue::Pointer, type->typeEntry()->qualifiedCppName()); if (Generator::isPointer(type)) - return QLatin1String("static_cast< ::") + type->typeEntry()->qualifiedCppName() + QLatin1String(" *>(0)"); + return DefaultValue(DefaultValue::Pointer, QLatin1String("::") + type->typeEntry()->qualifiedCppName()); if (type->typeEntry()->isComplex()) { - const ComplexTypeEntry* cType = reinterpret_cast<const ComplexTypeEntry*>(type->typeEntry()); - QString ctor = cType->defaultConstructor(); - if (!ctor.isEmpty()) - return ctor; - ctor = minimalConstructor(AbstractMetaClass::findClass(classes(), cType)); - if (type->hasInstantiations()) - ctor = ctor.replace(getFullTypeName(cType), getFullTypeNameWithoutModifiers(type)); + const ComplexTypeEntry* cType = static_cast<const ComplexTypeEntry*>(type->typeEntry()); + if (cType->hasDefaultConstructor()) + return DefaultValue(DefaultValue::Custom, cType->defaultConstructor()); + auto ctor = minimalConstructor(AbstractMetaClass::findClass(classes(), cType)); + if (ctor.isValid() && type->hasInstantiations()) { + QString v = ctor.value(); + v.replace(getFullTypeName(cType), getFullTypeNameWithoutModifiers(type)); + ctor.setValue(v); + } return ctor; } return minimalConstructor(type->typeEntry()); } -QString Generator::minimalConstructor(const TypeEntry* type) const +DefaultValue Generator::minimalConstructor(const TypeEntry* type) const { if (!type) - return QString(); + return DefaultValue(DefaultValue::Error); if (type->isCppPrimitive()) { const QString &name = type->qualifiedCppName(); return name == QLatin1String("bool") - ? QLatin1String("false") : name + QLatin1String("(0)"); + ? DefaultValue(DefaultValue::Boolean) + : DefaultValue(DefaultValue::CppScalar, name); } - if (type->isEnum()) - return QLatin1String("static_cast< ::") + type->qualifiedCppName() + QLatin1String(">(0)"); + if (type->isEnum()) { + const auto enumEntry = static_cast<const EnumTypeEntry *>(type); + if (const auto *nullValue = enumEntry->nullValue()) + return DefaultValue(DefaultValue::Enum, nullValue->name()); + return DefaultValue(DefaultValue::Custom, + QLatin1String("static_cast< ::") + type->qualifiedCppName() + + QLatin1String(">(0)")); + } - if (type->isFlags()) - return type->qualifiedCppName() + QLatin1String("(0)"); + if (type->isFlags()) { + return DefaultValue(DefaultValue::Custom, + type->qualifiedCppName() + QLatin1String("(0)")); + } if (type->isPrimitive()) { - QString ctor = reinterpret_cast<const PrimitiveTypeEntry*>(type)->defaultConstructor(); + QString ctor = static_cast<const PrimitiveTypeEntry*>(type)->defaultConstructor(); // If a non-C++ (i.e. defined by the user) primitive type does not have // a default constructor defined by the user, the empty constructor is // heuristically returned. If this is wrong the build of the generated // bindings will tell. return ctor.isEmpty() - ? (QLatin1String("::") + type->qualifiedCppName() + QLatin1String("()")) - : ctor; + ? DefaultValue(DefaultValue::DefaultConstructorWithDefaultValues, QLatin1String("::") + + type->qualifiedCppName()) + : DefaultValue(DefaultValue::Custom, ctor); } if (type->isComplex()) return minimalConstructor(AbstractMetaClass::findClass(classes(), type)); - return QString(); + return DefaultValue(DefaultValue::Error); } -QString Generator::minimalConstructor(const AbstractMetaClass* metaClass) const +static QString constructorCall(const QString &qualifiedCppName, const QStringList &args) +{ + return QLatin1String("::") + qualifiedCppName + QLatin1Char('(') + + args.join(QLatin1String(", ")) + QLatin1Char(')'); +} + +DefaultValue Generator::minimalConstructor(const AbstractMetaClass* metaClass) const { if (!metaClass) - return QString(); + return DefaultValue(DefaultValue::Error); - const ComplexTypeEntry* cType = reinterpret_cast<const ComplexTypeEntry*>(metaClass->typeEntry()); + const ComplexTypeEntry* cType = static_cast<const ComplexTypeEntry*>(metaClass->typeEntry()); if (cType->hasDefaultConstructor()) - return cType->defaultConstructor(); + return DefaultValue(DefaultValue::Custom, cType->defaultConstructor()); + const QString qualifiedCppName = cType->qualifiedCppName(); + // Obtain a list of constructors sorted by complexity and number of arguments + QMultiMap<int, const AbstractMetaFunction *> candidates; const AbstractMetaFunctionList &constructors = metaClass->queryFunctions(AbstractMetaClass::Constructors); - int maxArgs = 0; for (const AbstractMetaFunction *ctor : constructors) { - if (ctor->isUserAdded() || ctor->isPrivate() || ctor->functionType() != AbstractMetaFunction::ConstructorFunction) - continue; - - int numArgs = ctor->arguments().size(); - if (numArgs == 0) { - maxArgs = 0; - break; - } - if (numArgs > maxArgs) - maxArgs = numArgs; - } - - QString qualifiedCppName = metaClass->typeEntry()->qualifiedCppName(); - QStringList templateTypes; - const QVector<TypeEntry *> &templateArguments = metaClass->templateArguments(); - for (TypeEntry *templateType : templateArguments) - templateTypes << templateType->qualifiedCppName(); - - // Empty constructor. - if (maxArgs == 0) - return QLatin1String("::") + qualifiedCppName + QLatin1String("()"); - - QVector<const AbstractMetaFunction *> candidates; - - // Constructors with C++ primitive types, enums or pointers only. - // Start with the ones with fewer arguments. - for (int i = 1; i <= maxArgs; ++i) { - for (const AbstractMetaFunction *ctor : constructors) { - if (ctor->isUserAdded() || ctor->isPrivate() || ctor->functionType() != AbstractMetaFunction::ConstructorFunction) - continue; - - const AbstractMetaArgumentList &arguments = ctor->arguments(); - if (arguments.size() != i) - continue; - - QStringList args; - for (const AbstractMetaArgument *arg : arguments) { - const TypeEntry* type = arg->type()->typeEntry(); - if (type == metaClass->typeEntry()) { - args.clear(); - break; - } - - if (!arg->originalDefaultValueExpression().isEmpty()) { - if (!arg->defaultValueExpression().isEmpty() - && arg->defaultValueExpression() != arg->originalDefaultValueExpression()) { - args << arg->defaultValueExpression(); - } - break; - } - - if (type->isCppPrimitive() || type->isEnum() || isPointer(arg->type())) { - QString argValue = minimalConstructor(arg->type()); - if (argValue.isEmpty()) { - args.clear(); - break; - } - args << argValue; - } else { - args.clear(); - break; - } + if (!ctor->isUserAdded() && !ctor->isPrivate() + && ctor->functionType() == AbstractMetaFunction::ConstructorFunction) { + // No arguments: Default constructible + const auto &arguments = ctor->arguments(); + if (arguments.isEmpty()) { + return DefaultValue(DefaultValue::DefaultConstructor, + QLatin1String("::") + qualifiedCppName); } - - if (!args.isEmpty()) - return QString::fromLatin1("::%1(%2)").arg(qualifiedCppName, args.join(QLatin1String(", "))); - - candidates << ctor; + // First argument has unmodified default: Default constructible with values + if (arguments.constFirst()->hasUnmodifiedDefaultValueExpression()) { + return DefaultValue(DefaultValue::DefaultConstructorWithDefaultValues, + QLatin1String("::") + qualifiedCppName); + } + // Examine arguments, exclude functions taking a self parameter + bool simple = true; + bool suitable = true; + for (int i = 0, size = arguments.size(); + suitable && i < size && !arguments.at(i)->hasDefaultValueExpression(); ++i) { + const AbstractMetaArgument *arg = arguments.at(i); + const TypeEntry *aType = arg->type()->typeEntry(); + suitable &= aType != cType; + simple &= aType->isCppPrimitive() || aType->isEnum() || isPointer(arg->type()); + } + if (suitable) + candidates.insert(arguments.size() + (simple ? 0 : 100), ctor); } } - // Constructors with C++ primitive types, enums, pointers, value types, - // and user defined primitive types. - // Builds the minimal constructor recursively. - for (const AbstractMetaFunction *ctor : qAsConst(candidates)) { + for (auto it = candidates.cbegin(), end = candidates.cend(); it != end; ++it) { + const AbstractMetaArgumentList &arguments = it.value()->arguments(); QStringList args; - const AbstractMetaArgumentList &arguments = ctor->arguments(); - for (const AbstractMetaArgument *arg : arguments) { - if (arg->type()->typeEntry() == metaClass->typeEntry()) { - args.clear(); + bool ok = true; + for (int i =0, size = arguments.size(); ok && i < size; ++i) { + const AbstractMetaArgument *arg = arguments.at(i); + if (arg->hasDefaultValueExpression()) { + if (arg->hasModifiedDefaultValueExpression()) + args << arg->defaultValueExpression(); // Spell out modified values break; } - QString argValue = minimalConstructor(arg->type()); - if (argValue.isEmpty()) { - args.clear(); - break; - } - args << argValue; - } - if (!args.isEmpty()) { - return QString::fromLatin1("::%1(%2)").arg(qualifiedCppName, args.join(QLatin1String(", "))); + auto argValue = minimalConstructor(arg->type()); + ok &= argValue.isValid(); + args << argValue.constructorParameter(); } + if (ok) + return DefaultValue(DefaultValue::Custom, constructorCall(qualifiedCppName, args)); } - return QString(); + return DefaultValue(DefaultValue::Error); +} + +// Should int be used for a (protected) enum when generating the public wrapper? +bool Generator::useEnumAsIntForProtectedHack(const AbstractMetaType *metaType) const +{ + if (metaType->isFlags()) + return true; + if (!metaType->isEnum()) + return false; + const AbstractMetaEnum *metaEnum = findAbstractMetaEnum(metaType); + if (!metaEnum) + return true; + if (metaEnum->attributes() & AbstractMetaAttributes::Public) // No reason, type is public + return false; + // Only ordinary C-enums can be used as int, scoped enums fail when used + // as function arguments. + if (metaEnum->enumKind() == EnumKind::EnumClass) + qWarning(lcShiboken, "%s", qPrintable(msgCannotUseEnumAsInt(metaEnum->name()))); + return true; } QString Generator::translateType(const AbstractMetaType *cType, @@ -754,7 +818,7 @@ QString Generator::translateType(const AbstractMetaType *cType, s = QLatin1String("void"); } else if (cType->isArray()) { s = translateType(cType->arrayElementType(), context, options) + QLatin1String("[]"); - } else if (options & Generator::EnumAsInts && (cType->isEnum() || cType->isFlags())) { + } else if ((options & Generator::EnumAsInts) && useEnumAsIntForProtectedHack(cType)) { s = QLatin1String("int"); } else { if (options & Generator::OriginalName) { diff --git a/sources/shiboken2/generator/generator.h b/sources/shiboken2/generator/generator.h index 010ed868c..225e7aec7 100644 --- a/sources/shiboken2/generator/generator.h +++ b/sources/shiboken2/generator/generator.h @@ -57,7 +57,7 @@ class ContainerTypeEntry; class Indentor; QTextStream& formatCode(QTextStream &s, const QString& code, Indentor &indentor); -void verifyDirectoryFor(const QFile &file); +void verifyDirectoryFor(const QString &file); QString getClassTargetFullName(const AbstractMetaClass* metaClass, bool includePackageName = true); QString getClassTargetFullName(const AbstractMetaEnum* metaEnum, bool includePackageName = true); @@ -95,6 +95,42 @@ const int alwaysGenerateDestructor = 1; const int alwaysGenerateDestructor = 0; #endif +class DefaultValue +{ +public: + enum Type + { + Error, + Boolean, + CppScalar, // A C++ scalar type (int,..) specified by value() + Custom, // A custom constructor/expression, uses value() as is + DefaultConstructor, // For classes named value() + DefaultConstructorWithDefaultValues, // as DefaultConstructor, but can't return {} though. + Enum, // Enum value as specified by value() + Pointer, // Pointer of type value() + Void // "", for return values only + }; + + explicit DefaultValue(Type t = Error, QString value = QString()); + explicit DefaultValue(QString customValue); + + bool isValid() const { return m_type != Error; } + + QString returnValue() const; + QString initialization() const; + QString constructorParameter() const; + + QString value() const { return m_value; } + void setValue(const QString &value) { m_value = value; } + + Type type() const { return m_type; } + void setType(Type type) { m_type = type; } + +private: + Type m_type; + QString m_value; +}; + /** * A GeneratorContext object contains a pointer to an AbstractMetaClass and/or a specialized * AbstractMetaType, for which code is currently being generated. @@ -166,13 +202,74 @@ public: Generator(); virtual ~Generator(); - bool setup(const ApiExtractor& extractor, const QMap<QString, QString> args); + bool setup(const ApiExtractor& extractor); virtual OptionDescriptions options() const; + virtual bool handleOption(const QString &key, const QString &value); /// Returns the classes used to generate the binding code. AbstractMetaClassList classes() const; + /// Returns the output directory + QString outputDirectory() const; + + /// Set the output directory + void setOutputDirectory(const QString &outDir); + + /** + * Start the code generation, be sure to call setClasses before callign this method. + * For each class it creates a QTextStream, call the write method with the current + * class and the associated text stream, then write the text stream contents if needed. + * \see #write + */ + bool generate(); + + /// Returns the license comment to be prepended to each source file generated. + QString licenseComment() const; + + /// Sets the license comment to be prepended to each source file generated. + void setLicenseComment(const QString &licenseComment); + + /// Returns the generator's name. Used for cosmetic purposes. + virtual const char* name() const = 0; + + /** + * Retrieves the name of the currently processed module. + * While package name is a complete package idetification, e.g. 'PySide.QtCore', + * a module name represents the last part of the package, e.g. 'QtCore'. + * If the target language separates the modules with characters other than + * dots ('.') the generator subclass must overload this method. + * \return a string representing the last part of a package name + */ + QString moduleName() const; + + /** + * Retrieves a list of constructors used in implicit conversions + * available on the given type. The TypeEntry must be a value-type + * or else it will return an empty list. + * \param type a TypeEntry that is expected to be a value-type + * \return a list of constructors that could be used as implicit converters + */ + AbstractMetaFunctionList implicitConversions(const TypeEntry* type) const; + + /// Convenience function for implicitConversions(const TypeEntry* type). + AbstractMetaFunctionList implicitConversions(const AbstractMetaType* metaType) const; + + /// Check if type is a pointer. + static bool isPointer(const AbstractMetaType* type); + + /// Tells if the type or class is an Object (or QObject) Type. + static bool isObjectType(const TypeEntry* type); + static bool isObjectType(const ComplexTypeEntry* type); + static bool isObjectType(const AbstractMetaType* metaType); + static bool isObjectType(const AbstractMetaClass* metaClass); + + /// Returns true if the type is a C string (const char*). + static bool isCString(const AbstractMetaType* type); + /// Returns true if the type is a void pointer. + static bool isVoidPointer(const AbstractMetaType* type); + +protected: /// Returns the classes, topologically ordered, used to generate the binding code. /// /// The classes are ordered such that derived classes appear later in the list than @@ -191,33 +288,12 @@ public: /// Returns all container types found by APIExtractor ContainerTypeEntryList containerTypes() const; - /// Returns an AbstractMetaEnum for a given EnumTypeEntry, or NULL if not found. - const AbstractMetaEnum* findAbstractMetaEnum(const EnumTypeEntry* typeEntry) const; - /// Returns an AbstractMetaEnum for a given TypeEntry that is an EnumTypeEntry, or NULL if not found. const AbstractMetaEnum* findAbstractMetaEnum(const TypeEntry* typeEntry) const; - /// Returns an AbstractMetaEnum for the enum related to a given FlagsTypeEntry, or NULL if not found. - const AbstractMetaEnum* findAbstractMetaEnum(const FlagsTypeEntry* typeEntry) const; - /// Returns an AbstractMetaEnum for a given AbstractMetaType that holds an EnumTypeEntry, or NULL if not found. const AbstractMetaEnum* findAbstractMetaEnum(const AbstractMetaType* metaType) const; - /// Returns the output directory - QString outputDirectory() const; - - /// Set the output directory - void setOutputDirectory(const QString &outDir); - - /** - * Start the code generation, be sure to call setClasses before callign this method. - * For each class it creates a QTextStream, call the write method with the current - * class and the associated text stream, then write the text stream contents if needed. - * \see #write - */ - bool generate(); - - /// Generates a file for given AbstractMetaClass or AbstractMetaType (smart pointer case). bool generateFileForContext(GeneratorContext &context); @@ -225,9 +301,6 @@ public: QString getFileNameBaseForSmartPointer(const AbstractMetaType *smartPointerType, const AbstractMetaClass *smartPointerClass) const; - /// Returns the generator's name. Used for cosmetic purposes. - virtual const char* name() const = 0; - /// Returns true if the generator should generate any code for the TypeEntry. bool shouldGenerateTypeEntry(const TypeEntry*) const; @@ -266,56 +339,10 @@ public: void replaceTemplateVariables(QString &code, const AbstractMetaFunction *func); /** - * Returns the license comment to be prepended to each source file generated. - */ - QString licenseComment() const; - - /** - * Sets the license comment to be prepended to each source file generated. - */ - void setLicenseComment(const QString &licenseComment); - - /** * Returns the package name. */ QString packageName() const; - /** - * Retrieves the name of the currently processed module. - * While package name is a complete package idetification, e.g. 'PySide.QtCore', - * a module name represents the last part of the package, e.g. 'QtCore'. - * If the target language separates the modules with characters other than - * dots ('.') the generator subclass must overload this method. - * \return a string representing the last part of a package name - */ - virtual QString moduleName() const; - - /** - * Retrieves a list of constructors used in implicit conversions - * available on the given type. The TypeEntry must be a value-type - * or else it will return an empty list. - * \param type a TypeEntry that is expected to be a value-type - * \return a list of constructors that could be used as implicit converters - */ - AbstractMetaFunctionList implicitConversions(const TypeEntry* type) const; - - /// Convenience function for implicitConversions(const TypeEntry* type). - AbstractMetaFunctionList implicitConversions(const AbstractMetaType* metaType) const; - - /// Check if type is a pointer. - static bool isPointer(const AbstractMetaType* type); - - /// Tells if the type or class is an Object (or QObject) Type. - static bool isObjectType(const TypeEntry* type); - static bool isObjectType(const ComplexTypeEntry* type); - static bool isObjectType(const AbstractMetaType* metaType); - static bool isObjectType(const AbstractMetaClass* metaClass); - - /// Returns true if the type is a C string (const char*). - static bool isCString(const AbstractMetaType* type); - /// Returns true if the type is a void pointer. - static bool isVoidPointer(const AbstractMetaType* type); - // Returns the full name of the type. QString getFullTypeName(const TypeEntry* type) const; QString getFullTypeName(const AbstractMetaType* type) const; @@ -333,11 +360,10 @@ public: * It will check first for a user defined default constructor. * Returns a null string if it fails. */ - QString minimalConstructor(const TypeEntry* type) const; - QString minimalConstructor(const AbstractMetaType* type) const; - QString minimalConstructor(const AbstractMetaClass* metaClass) const; + DefaultValue minimalConstructor(const TypeEntry* type) const; + DefaultValue minimalConstructor(const AbstractMetaType* type) const; + DefaultValue minimalConstructor(const AbstractMetaClass* metaClass) const; -protected: /** * Returns the file name used to write the binding code of an AbstractMetaClass/Type. * \param context the GeneratorContext which contains an AbstractMetaClass or AbstractMetaType @@ -348,7 +374,7 @@ protected: virtual QString fileNameForContext(GeneratorContext &context) const = 0; - virtual bool doSetup(const QMap<QString, QString>& args) = 0; + virtual bool doSetup() = 0; /** * Write the bindding code for an AbstractMetaClass. @@ -378,6 +404,8 @@ protected: const QString &context); private: + bool useEnumAsIntForProtectedHack(const AbstractMetaType *cType) const; + struct GeneratorPrivate; GeneratorPrivate* m_d; void collectInstantiatedContainersAndSmartPointers(const AbstractMetaFunction* func); diff --git a/sources/shiboken2/generator/main.cpp b/sources/shiboken2/generator/main.cpp index 0b7c15244..362191fd0 100644 --- a/sources/shiboken2/generator/main.cpp +++ b/sources/shiboken2/generator/main.cpp @@ -34,6 +34,9 @@ #include <QtCore/QDir> #include <iostream> #include <apiextractor.h> +#include <fileout.h> +#include <typedatabase.h> +#include <messages.h> #include "generator.h" #include "shibokenconfig.h" #include "cppgenerator.h" @@ -41,9 +44,9 @@ #include "qtdocgenerator.h" #ifdef _WINDOWS - #define PATH_SPLITTER ";" +static const QChar pathSplitter = QLatin1Char(';'); #else - #define PATH_SPLITTER ":" +static const QChar pathSplitter = QLatin1Char(':'); #endif static inline QString languageLevelOption() { return QStringLiteral("language-level"); } @@ -52,95 +55,12 @@ static inline QString frameworkIncludePathOption() { return QStringLiteral("fram static inline QString systemIncludePathOption() { return QStringLiteral("system-include-paths"); } static inline QString typesystemPathOption() { return QStringLiteral("typesystem-paths"); } static inline QString helpOption() { return QStringLiteral("help"); } -static const char helpHint[] = "Note: use --help or -h for more information.\n"; - -namespace { - -class ArgsHandler -{ -public: - explicit ArgsHandler(const QMap<QString, QString>& other); - virtual ~ArgsHandler(); - - inline QMap<QString, QString>& args() const - { - return *m_args; - } - - inline bool argExists(const QString& s) const - { - return m_args->contains(s); - } - - QString removeArg(const QString& s); - bool argExistsRemove(const QString& s); +static inline QString diffOption() { return QStringLiteral("diff"); } +static inline QString dryrunOption() { return QStringLiteral("dry-run"); } - inline QString argValue(const QString& s) const - { - return m_args->value(s); - } - - inline bool noArgs() const - { - return m_args->isEmpty(); - } - - QString errorMessage() const; - -private: - QMap<QString, QString>* m_args; -}; - -ArgsHandler::ArgsHandler(const QMap<QString, QString>& other) - : m_args(new QMap<QString, QString>(other)) -{ -} - -ArgsHandler::~ArgsHandler() -{ - delete m_args; -} - -QString ArgsHandler::removeArg(const QString& s) -{ - QString retval; - - if (argExists(s)) { - retval = argValue(s); - m_args->remove(s); - } - - return retval; -} - -bool ArgsHandler::argExistsRemove(const QString& s) -{ - bool retval = false; - - if (argExists(s)) { - retval = true; - m_args->remove(s); - } +static const char helpHint[] = "Note: use --help or -h for more information.\n"; - return retval; -} - -QString ArgsHandler::errorMessage() const -{ - typedef QMap<QString, QString>::ConstIterator StringMapConstIt; - - QString message; - QTextStream str(&message); - str << "shiboken: Called with wrong arguments:"; - for (StringMapConstIt it = m_args->cbegin(), end = m_args->cend(); it != end; ++it) { - str << ' ' << it.key(); - if (!it.value().isEmpty()) - str << ' ' << it.value(); - } - str << "\nCommand line: " << QCoreApplication::arguments().join(QLatin1Char(' ')); - return message; -} -} +typedef QMap<QString, QString> CommandArgumentMap; typedef Generator::OptionDescriptions OptionDescriptions; @@ -214,18 +134,18 @@ static bool processProjectFile(QFile& projectFile, QMap<QString, QString>& args) } if (!includePaths.isEmpty()) - args.insert(includePathOption(), includePaths.join(QLatin1String(PATH_SPLITTER))); + args.insert(includePathOption(), includePaths.join(pathSplitter)); if (!frameworkIncludePaths.isEmpty()) args.insert(frameworkIncludePathOption(), - frameworkIncludePaths.join(QLatin1String(PATH_SPLITTER))); + frameworkIncludePaths.join(pathSplitter)); if (!systemIncludePaths.isEmpty()) { args.insert(systemIncludePathOption(), - systemIncludePaths.join(QLatin1String(PATH_SPLITTER))); + systemIncludePaths.join(pathSplitter)); } if (!typesystemPaths.isEmpty()) - args.insert(typesystemPathOption(), typesystemPaths.join(QLatin1String(PATH_SPLITTER))); + args.insert(typesystemPathOption(), typesystemPaths.join(pathSplitter)); if (!apiVersions.isEmpty()) args.insert(QLatin1String("api-version"), apiVersions.join(QLatin1Char('|'))); if (!languageLevel.isEmpty()) @@ -233,9 +153,9 @@ static bool processProjectFile(QFile& projectFile, QMap<QString, QString>& args) return true; } -static QMap<QString, QString> getInitializedArguments() +static CommandArgumentMap getInitializedArguments() { - QMap<QString, QString> args; + CommandArgumentMap args; QStringList arguments = QCoreApplication::arguments(); QString appName = arguments.constFirst(); arguments.removeFirst(); @@ -277,11 +197,11 @@ static QMap<QString, QString> getInitializedArguments() // Concatenate values of path arguments that can occur multiple times on the // command line. static void addPathOptionValue(const QString &option, const QString &value, - QMap<QString, QString> &args) + CommandArgumentMap &args) { - const QMap<QString, QString>::iterator it = args.find(option); + const CommandArgumentMap::iterator it = args.find(option); if (it != args.end()) - it.value().append(QLatin1String(PATH_SPLITTER) + value); + it.value().append(pathSplitter + value); else args.insert(option, value); } @@ -369,7 +289,9 @@ void printUsage() s << "Usage:\n " << "shiboken [options] header-file typesystem-file\n\n" << "General options:\n"; - const QString pathSyntax = QLatin1String("<path>[" PATH_SPLITTER "<path>" PATH_SPLITTER "...]"); + QString pathSyntax; + QTextStream(&pathSyntax) << "<path>[" << pathSplitter << "<path>" + << pathSplitter << "...]"; OptionDescriptions generalOptions = OptionDescriptions() << qMakePair(QLatin1String("api-version=<\"package mask\">,<\"version\">"), QLatin1String("Specify the supported api version used to generate the bindings")) @@ -380,18 +302,22 @@ void printUsage() << qMakePair(QLatin1String("drop-type-entries=\"<TypeEntry0>[;TypeEntry1;...]\""), QLatin1String("Semicolon separated list of type system entries (classes, namespaces,\n" "global functions and enums) to be dropped from generation.")) - << qMakePair(QLatin1String("-F") + pathSyntax, QString()) + << qMakePair(QLatin1String("-F<path>"), QString()) << qMakePair(QLatin1String("framework-include-paths=") + pathSyntax, QLatin1String("Framework include paths used by the C++ parser")) - << qMakePair(QLatin1String("-isystem") + pathSyntax, QString()) + << qMakePair(QLatin1String("-isystem<path>"), QString()) << qMakePair(QLatin1String("system-include-paths=") + pathSyntax, QLatin1String("System include paths used by the C++ parser")) << qMakePair(QLatin1String("generator-set=<\"generator module\">"), QLatin1String("generator-set to be used. e.g. qtdoc")) + << qMakePair(diffOption(), + QLatin1String("Print a diff of wrapper files")) + << qMakePair(dryrunOption(), + QLatin1String("Dry run, do not generate wrapper files")) << qMakePair(QLatin1String("-h"), QString()) << qMakePair(helpOption(), QLatin1String("Display this help and exit")) - << qMakePair(QLatin1String("-I") + pathSyntax, QString()) + << qMakePair(QLatin1String("-I<path>"), QString()) << qMakePair(QLatin1String("include-paths=") + pathSyntax, QLatin1String("Include paths used by the C++ parser")) << qMakePair(languageLevelOption() + QLatin1String("=, -std=<level>"), @@ -407,7 +333,7 @@ void printUsage() "Replaces and overrides command line arguments")) << qMakePair(QLatin1String("silent"), QLatin1String("Avoid printing any message")) - << qMakePair(QLatin1String("-T") + pathSyntax, QString()) + << qMakePair(QLatin1String("-T<path>"), QString()) << qMakePair(QLatin1String("typesystem-paths=") + pathSyntax, QLatin1String("Paths used when searching for typesystems")) << qMakePair(QLatin1String("version"), @@ -438,19 +364,15 @@ static inline void errorPrint(const QString& s) << "\nCommand line: " << qPrintable(arguments.join(QLatin1Char(' '))) << '\n'; } -static QString msgInvalidVersion(const QString &package, const QString &version) -{ - return QLatin1String("Invalid version \"") + version - + QLatin1String("\" specified for package ") + package + QLatin1Char('.'); -} - static void parseIncludePathOption(const QString &option, HeaderType headerType, - ArgsHandler &args, + CommandArgumentMap &args, ApiExtractor &extractor) { - const QString path = args.removeArg(option); - if (!path.isEmpty()) { - const QStringList includePathListList = path.split(QLatin1String(PATH_SPLITTER)); + const CommandArgumentMap::iterator it = args.find(option); + if (it != args.end()) { + const QStringList includePathListList = + it.value().split(pathSplitter, QString::SkipEmptyParts); + args.erase(it); for (const QString &s : includePathListList) extractor.addIncludePath(HeaderPath{QFile::encodeName(s), headerType}); } @@ -469,19 +391,24 @@ int main(int argc, char *argv[]) qCDebug(lcShiboken()).noquote().nospace() << QCoreApplication::arguments().join(QLatin1Char(' ')); // Store command arguments in a map - QMap<QString, QString> args = getCommandLineArgs(); - ArgsHandler argsHandler(args); + CommandArgumentMap args = getCommandLineArgs(); Generators generators; - if (argsHandler.argExistsRemove(QLatin1String("version"))) { + CommandArgumentMap::iterator ait = args.find(QLatin1String("version")); + if (ait != args.end()) { + args.erase(ait); printVerAndBanner(); return EXIT_SUCCESS; } - QString generatorSet = argsHandler.removeArg(QLatin1String("generator-set")); - // Also check QLatin1String("generatorSet") command line argument for backward compatibility. - if (generatorSet.isEmpty()) - generatorSet = argsHandler.removeArg(QLatin1String("generatorSet")); + QString generatorSet; + ait = args.find(QLatin1String("generator-set")); + if (ait == args.end()) // Also check QLatin1String("generatorSet") command line argument for backward compatibility. + ait = args.find(QLatin1String("generatorSet")); + if (ait != args.end()) { + generatorSet = ait.value(); + args.erase(ait); + } // Pre-defined generator sets. if (generatorSet == QLatin1String("qtdoc")) { @@ -497,28 +424,45 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - if (argsHandler.argExistsRemove(QLatin1String("help"))) { + ait = args.find(QLatin1String("help")); + if (ait != args.end()) { + args.erase(ait); printUsage(); return EXIT_SUCCESS; } + ait = args.find(diffOption()); + if (ait != args.end()) { + args.erase(ait); + FileOut::diff = true; + } + + ait = args.find(dryrunOption()); + if (ait != args.end()) { + args.erase(ait); + FileOut::dummy = true; + } + QString licenseComment; - QString licenseFileName = argsHandler.removeArg(QLatin1String("license-file")); - if (!licenseFileName.isEmpty()) { - if (QFile::exists(licenseFileName)) { - QFile licenseFile(licenseFileName); - if (licenseFile.open(QIODevice::ReadOnly)) - licenseComment = QString::fromUtf8(licenseFile.readAll()); + ait = args.find(QLatin1String("license-file")); + if (ait != args.end()) { + QFile licenseFile(ait.value()); + args.erase(ait); + if (licenseFile.open(QIODevice::ReadOnly)) { + licenseComment = QString::fromUtf8(licenseFile.readAll()); } else { - errorPrint(QStringLiteral("Couldn't find the file containing the license heading: %1"). - arg(licenseFileName)); + errorPrint(QStringLiteral("Could not open the file \"%1\" containing the license heading: %2"). + arg(QDir::toNativeSeparators(licenseFile.fileName()), licenseFile.errorString())); return EXIT_FAILURE; } } - QString outputDirectory = argsHandler.removeArg(QLatin1String("output-directory")); - if (outputDirectory.isEmpty()) - outputDirectory = QLatin1String("out"); + QString outputDirectory = QLatin1String("out"); + ait = args.find(QLatin1String("output-directory")); + if (ait != args.end()) { + outputDirectory = ait.value(); + args.erase(ait); + } if (!QDir(outputDirectory).exists()) { if (!QDir().mkpath(outputDirectory)) { @@ -532,11 +476,15 @@ int main(int argc, char *argv[]) ApiExtractor extractor; extractor.setLogDirectory(outputDirectory); - if (argsHandler.argExistsRemove(QLatin1String("silent"))) { + ait = args.find(QLatin1String("silent")); + if (ait != args.end()) { extractor.setSilent(true); + args.erase(ait); } else { - QString level = argsHandler.removeArg(QLatin1String("debug-level")); - if (!level.isEmpty()) { + ait = args.find(QLatin1String("debug-level")); + if (ait != args.end()) { + const QString level = ait.value(); + args.erase(ait); if (level == QLatin1String("sparse")) extractor.setDebugLevel(ReportHandler::SparseDebug); else if (level == QLatin1String("medium")) @@ -545,11 +493,15 @@ int main(int argc, char *argv[]) extractor.setDebugLevel(ReportHandler::FullDebug); } } - if (argsHandler.argExistsRemove(QLatin1String("no-suppress-warnings"))) + ait = args.find(QLatin1String("no-suppress-warnings")); + if (ait != args.end()) { + args.erase(ait); extractor.setSuppressWarnings(false); - - if (argsHandler.argExists(QLatin1String("api-version"))) { - const QStringList &versions = argsHandler.removeArg(QLatin1String("api-version")).split(QLatin1Char('|')); + } + ait = args.find(QLatin1String("api-version")); + if (ait != args.end()) { + const QStringList &versions = ait.value().split(QLatin1Char('|')); + args.erase(ait); for (const QString &fullVersion : versions) { QStringList parts = fullVersion.split(QLatin1Char(',')); QString package; @@ -563,54 +515,65 @@ int main(int argc, char *argv[]) } } - if (argsHandler.argExists(QLatin1String("drop-type-entries"))) - extractor.setDropTypeEntries(argsHandler.removeArg(QLatin1String("drop-type-entries"))); + ait = args.find(QLatin1String("drop-type-entries")); + if (ait != args.end()) { + extractor.setDropTypeEntries(ait.value()); + args.erase(ait); + } - QString path = argsHandler.removeArg(QLatin1String("typesystem-paths")); - if (!path.isEmpty()) - extractor.addTypesystemSearchPath(path.split(QLatin1String(PATH_SPLITTER))); + ait = args.find(QLatin1String("typesystem-paths")); + if (ait != args.end()) { + extractor.addTypesystemSearchPath(ait.value().split(pathSplitter)); + args.erase(ait); + } parseIncludePathOption(includePathOption(), HeaderType::Standard, - argsHandler, extractor); + args, extractor); parseIncludePathOption(frameworkIncludePathOption(), HeaderType::Framework, - argsHandler, extractor); + args, extractor); parseIncludePathOption(systemIncludePathOption(), HeaderType::System, - argsHandler, extractor); + args, extractor); - QString cppFileName = argsHandler.removeArg(QLatin1String("arg-1")); + ait = args.find(QLatin1String("arg-1")); + if (ait == args.end()) { + errorPrint(QLatin1String("Required argument header-file is missing.")); + return EXIT_FAILURE; + } + const QString cppFileName = ait.value(); + args.erase(ait); const QFileInfo cppFileNameFi(cppFileName); if (!cppFileNameFi.isFile() && !cppFileNameFi.isSymLink()) { errorPrint(QLatin1Char('"') + cppFileName + QLatin1String("\" does not exist.")); return EXIT_FAILURE; } - QString typeSystemFileName = argsHandler.removeArg(QLatin1String("arg-2")); + ait = args.find(QLatin1String("arg-2")); + if (ait == args.end()) { + errorPrint(QLatin1String("Required argument typesystem-file is missing.")); + return EXIT_FAILURE; + } + const QString typeSystemFileName = ait.value(); + args.erase(ait); QString messagePrefix = QFileInfo(typeSystemFileName).baseName(); if (messagePrefix.startsWith(QLatin1String("typesystem_"))) messagePrefix.remove(0, 11); ReportHandler::setPrefix(QLatin1Char('(') + messagePrefix + QLatin1Char(')')); - /* Make sure to remove the project file's arguments (if any) and - * --project-file, also the arguments of each generator before - * checking if there isn't any existing arguments in argsHandler. - */ - argsHandler.removeArg(QLatin1String("project-file")); - QMap<QString, QString> projectFileArgs = getInitializedArguments(); - if (!projectFileArgs.isEmpty()) { - QMap<QString, QString>::const_iterator it = - projectFileArgs.constBegin(); - for ( ; it != projectFileArgs.constEnd(); ++it) - argsHandler.removeArg(it.key()); - } - for (const GeneratorPtr &generator : qAsConst(generators)) { - const OptionDescriptions &options = generator->options(); - for (const auto &od : options) - argsHandler.removeArg(od.first); - } - - const QString languageLevel = argsHandler.removeArg(languageLevelOption()); - if (!languageLevel.isEmpty()) { - const QByteArray languageLevelBA = languageLevel.toLatin1(); + // Pass option to all generators (Cpp/Header generator have the same options) + for (ait = args.begin(); ait != args.end(); ) { + bool found = false; + for (const GeneratorPtr &generator : qAsConst(generators)) + found |= generator->handleOption(ait.key(), ait.value()); + if (found) + ait = args.erase(ait); + else + ++ait; + } + + ait = args.find(languageLevelOption()); + if (ait != args.end()) { + const QByteArray languageLevelBA = ait.value().toLatin1(); + args.erase(ait); const LanguageLevel level = clang::languageLevelFromOption(languageLevelBA.constData()); if (level == LanguageLevel::Default) { std::cout << "Invalid argument for language level: \"" @@ -620,8 +583,17 @@ int main(int argc, char *argv[]) extractor.setLanguageLevel(level); } - if (!argsHandler.noArgs()) { - errorPrint(argsHandler.errorMessage()); + /* Make sure to remove the project file's arguments (if any) and + * --project-file, also the arguments of each generator before + * checking if there isn't any existing arguments in argsHandler. + */ + args.remove(QLatin1String("project-file")); + CommandArgumentMap projectFileArgs = getInitializedArguments(); + for (auto it = projectFileArgs.cbegin(), end = projectFileArgs.cend(); it != end; ++it) + args.remove(it.key()); + + if (!args.isEmpty()) { + errorPrint(msgLeftOverArguments(args)); std::cout << helpHint; return EXIT_FAILURE; } @@ -642,17 +614,16 @@ int main(int argc, char *argv[]) if (!extractor.classCount()) qCWarning(lcShiboken) << "No C++ classes found!"; - qCDebug(lcShiboken) << extractor; + qCDebug(lcShiboken) << extractor << '\n' + << *TypeDatabase::instance(); for (const GeneratorPtr &g : qAsConst(generators)) { g->setOutputDirectory(outputDirectory); g->setLicenseComment(licenseComment); - if (g->setup(extractor, args)) { - if (!g->generate()) { - errorPrint(QLatin1String("Error running generator: ") - + QLatin1String(g->name()) + QLatin1Char('.')); - return EXIT_FAILURE; - } + if (!g->setup(extractor) || !g->generate()) { + errorPrint(QLatin1String("Error running generator: ") + + QLatin1String(g->name()) + QLatin1Char('.')); + return EXIT_FAILURE; } } diff --git a/sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp b/sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp index d47ba8bd7..19ddd5f37 100644 --- a/sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp +++ b/sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp @@ -28,6 +28,7 @@ #include "qtdocgenerator.h" #include <abstractmetalang.h> +#include <messages.h> #include <reporthandler.h> #include <typesystem.h> #include <qtdocparser.h> @@ -202,34 +203,6 @@ private: const QString &m_label; }; -static QString msgTagWarning(const QXmlStreamReader &reader, const QString &context, - const QString &tag, const QString &message) -{ - QString result; - QTextStream str(&result); - str << "While handling <"; - const QStringRef currentTag = reader.name(); - if (currentTag.isEmpty()) - str << tag; - else - str << currentTag; - str << "> in " << context << ", line "<< reader.lineNumber() - << ": " << message; - return result; -} - -static QString msgFallbackWarning(const QXmlStreamReader &reader, const QString &context, - const QString &tag, const QString &location, const QString &identifier, - const QString &fallback) -{ - QString message = QLatin1String("Falling back to \"") - + QDir::toNativeSeparators(fallback) + QLatin1String("\" for \"") + location - + QLatin1Char('"'); - if (!identifier.isEmpty()) - message += QLatin1String(" [") + identifier + QLatin1Char(']'); - return msgTagWarning(reader, context, tag, message); -} - struct QtXmlToSphinx::LinkContext { enum Type @@ -305,7 +278,7 @@ QTextStream &operator<<(QTextStream &str, const QtXmlToSphinx::LinkContext &link } QtXmlToSphinx::QtXmlToSphinx(QtDocGenerator* generator, const QString& doc, const QString& context) - : m_context(context), m_generator(generator), m_insideBold(false), m_insideItalic(false) + : m_tableHasHeader(false), m_context(context), m_generator(generator), m_insideBold(false), m_insideItalic(false) { m_handlerMap.insert(QLatin1String("heading"), &QtXmlToSphinx::handleHeadingTag); m_handlerMap.insert(QLatin1String("brief"), &QtXmlToSphinx::handleParaTag); @@ -1302,7 +1275,7 @@ bool QtXmlToSphinx::convertToRst(QtDocGenerator *generator, QFile sourceFile(sourceFileName); if (!sourceFile.open(QIODevice::ReadOnly | QIODevice::Text)) { if (errorMessage) - *errorMessage = FileOut::msgCannotOpenForReading(sourceFile); + *errorMessage = msgCannotOpenForReading(sourceFile); return false; } const QString doc = QString::fromUtf8(sourceFile.readAll()); @@ -1325,7 +1298,13 @@ void QtXmlToSphinx::Table::normalize() //QDoc3 generates tables with wrong number of columns. We have to //check and if necessary, merge the last columns. - int maxCols = self.at(0).count(); + int maxCols = -1; + for (const auto &row : qAsConst(self)) { + if (row.count() > maxCols) + maxCols = row.count(); + } + if (maxCols <= 0) + return; // add col spans for (row = 0; row < count(); ++row) { for (col = 0; col < at(row).count(); ++col) { @@ -1513,11 +1492,10 @@ QString QtDocGenerator::fileNameForContext(GeneratorContext &context) const const AbstractMetaClass *metaClass = context.metaClass(); if (!context.forSmartPointer()) { return getClassTargetFullName(metaClass, false) + fileNameSuffix(); - } else { - const AbstractMetaType *smartPointerType = context.preciseType(); - QString fileNameBase = getFileNameBaseForSmartPointer(smartPointerType, metaClass); - return fileNameBase + fileNameSuffix(); } + const AbstractMetaType *smartPointerType = context.preciseType(); + QString fileNameBase = getFileNameBaseForSmartPointer(smartPointerType, metaClass); + return fileNameBase + fileNameSuffix(); } void QtDocGenerator::writeFormattedText(QTextStream &s, const Documentation &doc, @@ -1534,7 +1512,7 @@ void QtDocGenerator::writeFormattedText(QTextStream &s, const Documentation &doc } else { const QString &value = doc.value(); const QVector<QStringRef> lines = value.splitRef(QLatin1Char('\n')); - int typesystemIndentation = std::numeric_limits<int>().max(); + int typesystemIndentation = std::numeric_limits<int>::max(); // check how many spaces must be removed from the beginning of each line for (const QStringRef &line : lines) { const auto it = std::find_if(line.cbegin(), line.cend(), @@ -1542,7 +1520,7 @@ void QtDocGenerator::writeFormattedText(QTextStream &s, const Documentation &doc if (it != line.cend()) typesystemIndentation = qMin(typesystemIndentation, int(it - line.cbegin())); } - if (typesystemIndentation == std::numeric_limits<int>().max()) + if (typesystemIndentation == std::numeric_limits<int>::max()) typesystemIndentation = 0; for (const QStringRef &line : lines) { s << INDENT @@ -1677,7 +1655,7 @@ void QtDocGenerator::writeFunctionList(QTextStream& s, const AbstractMetaClass* functionList << str; } - if ((functionList.size() > 0) || (staticFunctionList.size() > 0)) { + if (!functionList.isEmpty() || !staticFunctionList.isEmpty()) { QtXmlToSphinx::Table functionTable; s << endl @@ -1694,7 +1672,7 @@ void QtDocGenerator::writeFunctionList(QTextStream& s, const AbstractMetaClass* void QtDocGenerator::writeFunctionBlock(QTextStream& s, const QString& title, QStringList& functions) { - if (functions.size() > 0) { + if (!functions.isEmpty()) { s << title << endl << QString(title.size(), QLatin1Char('^')) << endl; @@ -1777,7 +1755,8 @@ void QtDocGenerator::writeConstructors(QTextStream& s, const AbstractMetaClass* writeFormattedText(s, func->documentation(), cppClass); } -QString QtDocGenerator::parseArgDocStyle(const AbstractMetaClass* cppClass, const AbstractMetaFunction* func) +QString QtDocGenerator::parseArgDocStyle(const AbstractMetaClass* /* cppClass */, + const AbstractMetaFunction* func) { QString ret; int optArgs = 0; @@ -1859,8 +1838,7 @@ void QtDocGenerator::writeDocSnips(QTextStream &s, if (row.trimmed().size() == 0) { if (currenRow == 0) continue; - else - s << endl; + s << endl; } if (currenRow == 0) { @@ -2124,7 +2102,7 @@ void QtDocGenerator::writeModuleDocumentation() s << ".. module:: " << it.key() << endl << endl; - QString title = it.key(); + const QString &title = it.key(); s << title << endl; s << Pad('*', title.length()) << endl << endl; @@ -2201,7 +2179,7 @@ void QtDocGenerator::writeAdditionalDocumentation() QFile additionalDocumentationFile(m_additionalDocumentationList); if (!additionalDocumentationFile.open(QIODevice::ReadOnly | QIODevice::Text)) { qCWarning(lcShiboken, "%s", - qPrintable(FileOut::msgCannotOpenForReading(additionalDocumentationFile))); + qPrintable(msgCannotOpenForReading(additionalDocumentationFile))); return; } @@ -2261,32 +2239,28 @@ void QtDocGenerator::writeAdditionalDocumentation() successCount, count); } -bool QtDocGenerator::doSetup(const QMap<QString, QString>& args) -{ - m_libSourceDir = args.value(QLatin1String("library-source-dir")); - m_docDataDir = args.value(QLatin1String("documentation-data-dir")); #ifdef __WIN32__ # define PATH_SEP ';' #else # define PATH_SEP ':' #endif - m_codeSnippetDirs = args.value(QLatin1String("documentation-code-snippets-dir"), m_libSourceDir).split(QLatin1Char(PATH_SEP)); - m_extraSectionDir = args.value(QLatin1String("documentation-extra-sections-dir")); - m_docParser = args.value(QLatin1String("doc-parser")) == QLatin1String("doxygen") - ? static_cast<DocParser*>(new DoxygenParser) - : static_cast<DocParser*>(new QtDocParser); - qCDebug(lcShiboken).noquote().nospace() << "doc-parser: " << args.value(QLatin1String("doc-parser")); +bool QtDocGenerator::doSetup() +{ + if (m_codeSnippetDirs.isEmpty()) + m_codeSnippetDirs = m_libSourceDir.split(QLatin1Char(PATH_SEP)); + + if (!m_docParser) + m_docParser = new QtDocParser; if (m_libSourceDir.isEmpty() || m_docDataDir.isEmpty()) { qCWarning(lcShiboken) << "Documentation data dir and/or Qt source dir not informed, " "documentation will not be extracted from Qt sources."; return false; - } else { - m_docParser->setDocumentationDataDirectory(m_docDataDir); - m_docParser->setLibrarySourceDirectory(m_libSourceDir); } - m_additionalDocumentationList = args.value(additionalDocumentationOption()); + + m_docParser->setDocumentationDataDirectory(m_docDataDir); + m_docParser->setLibrarySourceDirectory(m_libSourceDir); return true; } @@ -2294,19 +2268,49 @@ bool QtDocGenerator::doSetup(const QMap<QString, QString>& args) Generator::OptionDescriptions QtDocGenerator::options() const { return OptionDescriptions() - << qMakePair(QLatin1String("doc-parser"), + << qMakePair(QLatin1String("doc-parser=<parser>"), QLatin1String("The documentation parser used to interpret the documentation\n" "input files (qdoc|doxygen)")) - << qMakePair(QLatin1String("documentation-code-snippets-dir"), + << qMakePair(QLatin1String("documentation-code-snippets-dir=<dir>"), QLatin1String("Directory used to search code snippets used by the documentation")) - << qMakePair(QLatin1String("documentation-data-dir"), + << qMakePair(QLatin1String("documentation-data-dir=<dir>"), QLatin1String("Directory with XML files generated by documentation tool")) - << qMakePair(QLatin1String("documentation-extra-sections-dir"), + << qMakePair(QLatin1String("documentation-extra-sections-dir=<dir>"), QLatin1String("Directory used to search for extra documentation sections")) - << qMakePair(QLatin1String("library-source-dir"), + << qMakePair(QLatin1String("library-source-dir=<dir>"), QLatin1String("Directory where library source code is located")) - << qMakePair(additionalDocumentationOption(), + << qMakePair(additionalDocumentationOption() + QLatin1String("=<file>"), QLatin1String("List of additional XML files to be converted to .rst files\n" "(for example, tutorials).")); } +bool QtDocGenerator::handleOption(const QString &key, const QString &value) +{ + if (key == QLatin1String("library-source-dir")) { + m_libSourceDir = value; + return true; + } + if (key == QLatin1String("documentation-data-dir")) { + m_docDataDir = value; + return true; + } + if (key == QLatin1String("documentation-code-snippets-dir")) { + m_codeSnippetDirs = value.split(QLatin1Char(PATH_SEP)); + return true; + } + if (key == QLatin1String("documentation-extra-sections-dir")) { + m_extraSectionDir = value; + return true; + } + if (key == QLatin1String("doc-parser")) { + qCDebug(lcShiboken).noquote().nospace() << "doc-parser: " << value; + if (value == QLatin1String("doxygen")) + m_docParser = new DoxygenParser; + return true; + } + if (key == additionalDocumentationOption()) { + m_additionalDocumentationList = value; + return true; + } + return false; +} diff --git a/sources/shiboken2/generator/qtdoc/qtdocgenerator.h b/sources/shiboken2/generator/qtdoc/qtdocgenerator.h index e467abe90..5545de9a9 100644 --- a/sources/shiboken2/generator/qtdoc/qtdocgenerator.h +++ b/sources/shiboken2/generator/qtdoc/qtdocgenerator.h @@ -209,7 +209,7 @@ public: QString docDataDir() const { return m_docDataDir; } - bool doSetup(const QMap<QString, QString>& args) override; + bool doSetup() override; const char* name() const override { @@ -217,15 +217,15 @@ public: } OptionDescriptions options() const override; + bool handleOption(const QString &key, const QString &value) override; QStringList codeSnippetDirs() const { return m_codeSnippetDirs; } - bool shouldGenerate(const AbstractMetaClass *) const override; - protected: + bool shouldGenerate(const AbstractMetaClass *) const override; QString fileNameSuffix() const override; QString fileNameForContext(GeneratorContext &context) const override; void generateClass(QTextStream &s, GeneratorContext &classContext) override; diff --git a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp index 9e1d6926e..99947d347 100644 --- a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp +++ b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp @@ -29,8 +29,10 @@ #include <memory> #include "cppgenerator.h" +#include "fileout.h" #include "overloaddata.h" #include <abstractmetalang.h> +#include <messages.h> #include <reporthandler.h> #include <typedatabase.h> @@ -41,6 +43,8 @@ #include <QtCore/QDebug> #include <QMetaType> +static const char CPP_ARG0[] = "cppArg0"; + QHash<QString, QString> CppGenerator::m_nbFuncs = QHash<QString, QString>(); QHash<QString, QString> CppGenerator::m_sqFuncs = QHash<QString, QString>(); QHash<QString, QString> CppGenerator::m_mpFuncs = QHash<QString, QString>(); @@ -58,6 +62,28 @@ inline AbstractMetaType* getTypeWithoutContainer(AbstractMetaType* arg) return arg; } +// A helper for writing C++ return statements for either void ("return;") +// or some return value ("return value;") +class returnStatement +{ +public: + explicit returnStatement(QString s) : m_returnValue(std::move(s)) {} + + friend QTextStream &operator<<(QTextStream &s, const returnStatement &r); + +private: + const QString m_returnValue; +}; + +QTextStream &operator<<(QTextStream &s, const returnStatement &r) +{ + s << "return"; + if (!r.m_returnValue.isEmpty()) + s << ' ' << r.m_returnValue; + s << ';'; + return s; +} + CppGenerator::CppGenerator() { // Number protocol structure members names @@ -87,27 +113,27 @@ CppGenerator::CppGenerator() m_nbFuncs.insert(QLatin1String("bool"), QLatin1String("nb_nonzero")); // sequence protocol functions - typedef QPair<QString, QString> StrPair; m_sequenceProtocol.insert(QLatin1String("__len__"), - StrPair(QLatin1String("PyObject* " PYTHON_SELF_VAR), QLatin1String("Py_ssize_t"))); + {QLatin1String("PyObject* self"), + QLatin1String("Py_ssize_t")}); m_sequenceProtocol.insert(QLatin1String("__getitem__"), - StrPair(QLatin1String("PyObject* " PYTHON_SELF_VAR ", Py_ssize_t _i"), - QLatin1String("PyObject*"))); + {QLatin1String("PyObject* self, Py_ssize_t _i"), + QLatin1String("PyObject*")}); m_sequenceProtocol.insert(QLatin1String("__setitem__"), - StrPair(QLatin1String("PyObject* " PYTHON_SELF_VAR ", Py_ssize_t _i, PyObject* _value"), - QLatin1String("int"))); + {QLatin1String("PyObject* self, Py_ssize_t _i, PyObject* _value"), + QLatin1String("int")}); m_sequenceProtocol.insert(QLatin1String("__getslice__"), - StrPair(QLatin1String("PyObject* " PYTHON_SELF_VAR ", Py_ssize_t _i1, Py_ssize_t _i2"), - QLatin1String("PyObject*"))); + {QLatin1String("PyObject* self, Py_ssize_t _i1, Py_ssize_t _i2"), + QLatin1String("PyObject*")}); m_sequenceProtocol.insert(QLatin1String("__setslice__"), - StrPair(QLatin1String("PyObject* " PYTHON_SELF_VAR ", Py_ssize_t _i1, Py_ssize_t _i2, PyObject* _value"), - QLatin1String("int"))); + {QLatin1String("PyObject* self, Py_ssize_t _i1, Py_ssize_t _i2, PyObject* _value"), + QLatin1String("int")}); m_sequenceProtocol.insert(QLatin1String("__contains__"), - StrPair(QLatin1String("PyObject* " PYTHON_SELF_VAR ", PyObject* _value"), - QLatin1String("int"))); + {QLatin1String("PyObject* self, PyObject* _value"), + QLatin1String("int")}); m_sequenceProtocol.insert(QLatin1String("__concat__"), - StrPair(QLatin1String("PyObject* " PYTHON_SELF_VAR ", PyObject* _other"), - QLatin1String("PyObject*"))); + {QLatin1String("PyObject* self, PyObject* _other"), + QLatin1String("PyObject*")}); // Sequence protocol structure members names m_sqFuncs.insert(QLatin1String("__concat__"), QLatin1String("sq_concat")); @@ -120,14 +146,14 @@ CppGenerator::CppGenerator() // mapping protocol function m_mappingProtocol.insert(QLatin1String("__mlen__"), - StrPair(QLatin1String("PyObject* " PYTHON_SELF_VAR), - QLatin1String("Py_ssize_t"))); + {QLatin1String("PyObject* self"), + QLatin1String("Py_ssize_t")}); m_mappingProtocol.insert(QLatin1String("__mgetitem__"), - StrPair(QLatin1String("PyObject* " PYTHON_SELF_VAR ", PyObject* _key"), - QLatin1String("PyObject*"))); + {QLatin1String("PyObject* self, PyObject* _key"), + QLatin1String("PyObject*")}); m_mappingProtocol.insert(QLatin1String("__msetitem__"), - StrPair(QLatin1String("PyObject* " PYTHON_SELF_VAR ", PyObject* _key, PyObject* _value"), - QLatin1String("int"))); + {QLatin1String("PyObject* self, PyObject* _key, PyObject* _value"), + QLatin1String("int")}); // Sequence protocol structure members names m_mpFuncs.insert(QLatin1String("__mlen__"), QLatin1String("mp_length")); @@ -186,18 +212,19 @@ QVector<AbstractMetaFunctionList> CppGenerator::filterGroupedOperatorFunctions(c return result; } -bool CppGenerator::hasBoolCast(const AbstractMetaClass* metaClass) const +const AbstractMetaFunction *CppGenerator::boolCast(const AbstractMetaClass* metaClass) const { if (!useIsNullAsNbNonZero()) - return false; + return nullptr; // TODO: This could be configurable someday const AbstractMetaFunction* func = metaClass->findFunction(QLatin1String("isNull")); if (!func || !func->type() || !func->type()->typeEntry()->isPrimitive() || !func->isPublic()) - return false; + return nullptr; const PrimitiveTypeEntry* pte = static_cast<const PrimitiveTypeEntry*>(func->type()->typeEntry()); while (pte->referencedTypeEntry()) pte = pte->referencedTypeEntry(); - return func && func->isConstant() && pte->name() == QLatin1String("bool") && func->arguments().isEmpty(); + return func && func->isConstant() && pte->name() == QLatin1String("bool") + && func->arguments().isEmpty() ? func : nullptr; } typedef QMap<QString, AbstractMetaFunctionList> FunctionGroupMap; @@ -219,6 +246,21 @@ static QString chopType(QString s) return s; } +// Helper for field setters: Check for "const QWidget *" (settable field), +// but not "int *const" (read-only field). +static bool isPointerToConst(const AbstractMetaType *t) +{ + const AbstractMetaType::Indirections &indirections = t->indirectionsV(); + return t->isConstant() && !indirections.isEmpty() + && indirections.constLast() != Indirection::ConstPointer; +} + +static inline bool canGenerateFieldSetter(const AbstractMetaField *field) +{ + const AbstractMetaType *type = field->type(); + return !type->isConstant() || isPointerToConst(type); +} + /*! Function used to write the class generated binding code on the buffer \param s the output buffer @@ -260,6 +302,8 @@ void CppGenerator::generateClass(QTextStream &s, GeneratorContext &classContext) // needs the 'set' class from C++ STL. if (hasMultipleInheritanceInAncestry(metaClass)) s << "#include <set>" << endl; + if (metaClass->generateExceptionHandling()) + s << "#include <exception>" << endl; s << endl << "// module include" << endl << "#include \"" << getModuleHeaderFileName() << '"' << endl; @@ -315,7 +359,7 @@ void CppGenerator::generateClass(QTextStream &s, GeneratorContext &classContext) static_cast<const SmartPointerTypeEntry *>(classContext.preciseType() ->typeEntry()); QString rawGetter = typeEntry->getter(); - s << "static const char * " SMART_POINTER_GETTER " = \"" << rawGetter << "\";"; + s << "static const char * " << SMART_POINTER_GETTER << " = \"" << rawGetter << "\";"; } // class inject-code native/beginning @@ -459,7 +503,7 @@ void CppGenerator::generateClass(QTextStream &s, GeneratorContext &classContext) if (metaClass->typeEntry()->isValue() || metaClass->typeEntry()->isSmartPointer()) { writeCopyFunction(s, classContext); - signatureStream << metaClass->fullName() << ".__copy__()" << endl; + signatureStream << fullPythonClassName(metaClass) << ".__copy__()" << endl; } // Write single method definitions @@ -490,16 +534,20 @@ void CppGenerator::generateClass(QTextStream &s, GeneratorContext &classContext) } } - if (hasBoolCast(metaClass)) { + if (const AbstractMetaFunction *f = boolCast(metaClass)) { ErrorCode errorCode(-1); - s << "static int " << cpythonBaseName(metaClass) << "___nb_bool(PyObject* " PYTHON_SELF_VAR ")" << endl; + s << "static int " << cpythonBaseName(metaClass) << "___nb_bool(PyObject* self)" << endl; s << '{' << endl; writeCppSelfDefinition(s, classContext); - s << INDENT << "int result;" << endl; - s << INDENT << BEGIN_ALLOW_THREADS << endl; - s << INDENT << "result = !" CPP_SELF_VAR "->isNull();" << endl; - s << INDENT << END_ALLOW_THREADS << endl; - s << INDENT << "return result;" << endl; + if (f->allowThread()) { + s << INDENT << "int result;" << endl; + s << INDENT << BEGIN_ALLOW_THREADS << endl; + s << INDENT << "result = !" << CPP_SELF_VAR << "->isNull();" << endl; + s << INDENT << END_ALLOW_THREADS << endl; + s << INDENT << "return result;" << endl; + } else { + s << INDENT << "return !" << CPP_SELF_VAR << "->isNull();" << endl; + } s << '}' << endl << endl; } @@ -546,7 +594,7 @@ void CppGenerator::generateClass(QTextStream &s, GeneratorContext &classContext) if (metaField->isStatic()) continue; writeGetterFunction(s, metaField, classContext); - if (!metaField->type()->isConstant()) + if (canGenerateFieldSetter(metaField)) writeSetterFunction(s, metaField, classContext); s << endl; } @@ -557,10 +605,12 @@ void CppGenerator::generateClass(QTextStream &s, GeneratorContext &classContext) if (metaField->isStatic()) continue; - bool hasSetter = !metaField->type()->isConstant(); s << INDENT << "{const_cast<char*>(\"" << metaField->name() << "\"), "; - s << cpythonGetterFunctionName(metaField); - s << ", " << (hasSetter ? cpythonSetterFunctionName(metaField) : QLatin1String("0")); + s << cpythonGetterFunctionName(metaField) << ", "; + if (canGenerateFieldSetter(metaField)) + s << cpythonSetterFunctionName(metaField); + else + s << '0'; s << "}," << endl; } s << INDENT << "{0} // Sentinel" << endl; @@ -686,7 +736,7 @@ void CppGenerator::writeVirtualMethodNative(QTextStream&s, const AbstractMetaFun Indentation indentation(INDENT); - QString defaultReturnExpr; + DefaultValue defaultReturnExpr; if (retType) { const FunctionModificationList &mods = func->modifications(); for (const FunctionModification &mod : mods) { @@ -694,9 +744,9 @@ void CppGenerator::writeVirtualMethodNative(QTextStream&s, const AbstractMetaFun if (argMod.index == 0 && !argMod.replacedDefaultExpression.isEmpty()) { static const QRegularExpression regex(QStringLiteral("%(\\d+)")); Q_ASSERT(regex.isValid()); - defaultReturnExpr = argMod.replacedDefaultExpression; + QString expr = argMod.replacedDefaultExpression; for (int offset = 0; ; ) { - const QRegularExpressionMatch match = regex.match(defaultReturnExpr, offset); + const QRegularExpressionMatch match = regex.match(expr, offset); if (!match.hasMatch()) break; const int argId = match.capturedRef(1).toInt() - 1; @@ -704,23 +754,27 @@ void CppGenerator::writeVirtualMethodNative(QTextStream&s, const AbstractMetaFun qCWarning(lcShiboken) << "The expression used in return value contains an invalid index."; break; } - defaultReturnExpr.replace(match.captured(0), func->arguments().at(argId)->name()); + expr.replace(match.captured(0), func->arguments().at(argId)->name()); offset = match.capturedStart(1); } + defaultReturnExpr.setType(DefaultValue::Custom); + defaultReturnExpr.setValue(expr); } } } - if (defaultReturnExpr.isEmpty()) + if (!defaultReturnExpr.isValid()) defaultReturnExpr = minimalConstructor(func->type()); - if (defaultReturnExpr.isEmpty()) { + if (!defaultReturnExpr.isValid()) { QString errorMsg = QLatin1String(__FUNCTION__) + QLatin1String(": "); if (const AbstractMetaClass *c = func->implementingClass()) errorMsg += c->qualifiedCppName() + QLatin1String("::"); errorMsg += func->signature(); - errorMsg = ShibokenGenerator::msgCouldNotFindMinimalConstructor(errorMsg, func->type()->cppSignature()); + errorMsg = msgCouldNotFindMinimalConstructor(errorMsg, func->type()->cppSignature()); qCWarning(lcShiboken).noquote().nospace() << errorMsg; s << endl << INDENT << "#error " << errorMsg << endl; } + } else { + defaultReturnExpr.setType(DefaultValue::Void); } if (func->isAbstract() && func->isModifiedRemoved()) { @@ -728,7 +782,7 @@ void CppGenerator::writeVirtualMethodNative(QTextStream&s, const AbstractMetaFun << QString::fromLatin1("Pure virtual method '%1::%2' must be implement but was "\ "completely removed on type system.") .arg(func->ownerClass()->name(), func->minimalSignature()); - s << INDENT << "return " << defaultReturnExpr << ';' << endl; + s << INDENT << returnStatement(defaultReturnExpr.returnValue()) << endl; s << '}' << endl << endl; return; } @@ -747,13 +801,13 @@ void CppGenerator::writeVirtualMethodNative(QTextStream&s, const AbstractMetaFun s << INDENT << "if (PyErr_Occurred())" << endl; { Indentation indentation(INDENT); - s << INDENT << "return " << defaultReturnExpr << ';' << endl; + s << INDENT << returnStatement(defaultReturnExpr.returnValue()) << endl; } - s << INDENT << "Shiboken::AutoDecRef " PYTHON_OVERRIDE_VAR "(Shiboken::BindingManager::instance().getOverride(this, \""; + s << INDENT << "Shiboken::AutoDecRef " << PYTHON_OVERRIDE_VAR << "(Shiboken::BindingManager::instance().getOverride(this, \""; s << funcName << "\"));" << endl; - s << INDENT << "if (" PYTHON_OVERRIDE_VAR ".isNull()) {" << endl; + s << INDENT << "if (" << PYTHON_OVERRIDE_VAR << ".isNull()) {" << endl; { Indentation indentation(INDENT); CodeSnipList snips; @@ -768,7 +822,9 @@ void CppGenerator::writeVirtualMethodNative(QTextStream&s, const AbstractMetaFun s << INDENT << "PyErr_SetString(PyExc_NotImplementedError, \"pure virtual method '"; s << func->ownerClass()->name() << '.' << funcName; s << "()' not implemented.\");" << endl; - s << INDENT << "return " << (retType ? defaultReturnExpr : QString()); + s << INDENT << "return"; + if (retType) + s << ' ' << defaultReturnExpr.returnValue(); } else { s << INDENT << "gil.release();" << endl; s << INDENT; @@ -785,7 +841,7 @@ void CppGenerator::writeVirtualMethodNative(QTextStream&s, const AbstractMetaFun writeConversionRule(s, func, TypeSystem::TargetLangCode); - s << INDENT << "Shiboken::AutoDecRef " PYTHON_ARGS "("; + s << INDENT << "Shiboken::AutoDecRef " << PYTHON_ARGS << "("; if (func->arguments().isEmpty() || allArgumentsRemoved(func)) { s << "PyTuple_New(0));" << endl; @@ -844,7 +900,7 @@ void CppGenerator::writeVirtualMethodNative(QTextStream&s, const AbstractMetaFun if (argMod.resetAfterUse && !invalidateArgs.contains(argMod.index)) { invalidateArgs.insert(argMod.index); s << INDENT << "bool invalidateArg" << argMod.index; - s << " = PyTuple_GET_ITEM(" PYTHON_ARGS ", " << argMod.index - 1 << ")->ob_refcnt == 1;" << endl; + s << " = PyTuple_GET_ITEM(" << PYTHON_ARGS << ", " << argMod.index - 1 << ")->ob_refcnt == 1;" << endl; } else if (argMod.index == 0 && argMod.ownerships[TypeSystem::TargetLangCode] == TypeSystem::CppOwnership) { invalidateReturn = true; } @@ -866,36 +922,36 @@ void CppGenerator::writeVirtualMethodNative(QTextStream&s, const AbstractMetaFun if (!injectedCodeCallsPythonOverride(func)) { s << INDENT; - s << "Shiboken::AutoDecRef " PYTHON_RETURN_VAR "(PyObject_Call(" PYTHON_OVERRIDE_VAR ", " PYTHON_ARGS ", NULL));" << endl; + s << "Shiboken::AutoDecRef " << PYTHON_RETURN_VAR << "(PyObject_Call(" << PYTHON_OVERRIDE_VAR << ", " << PYTHON_ARGS << ", NULL));" << endl; s << INDENT << "// An error happened in python code!" << endl; - s << INDENT << "if (" PYTHON_RETURN_VAR ".isNull()) {" << endl; + s << INDENT << "if (" << PYTHON_RETURN_VAR << ".isNull()) {" << endl; { Indentation indent(INDENT); s << INDENT << "PyErr_Print();" << endl; - s << INDENT << "return " << defaultReturnExpr << ';' << endl; + s << INDENT << returnStatement(defaultReturnExpr.returnValue()) << endl; } s << INDENT << '}' << endl; if (retType) { if (invalidateReturn) - s << INDENT << "bool invalidateArg0 = " PYTHON_RETURN_VAR "->ob_refcnt == 1;" << endl; + s << INDENT << "bool invalidateArg0 = " << PYTHON_RETURN_VAR << "->ob_refcnt == 1;" << endl; if (func->typeReplaced(0) != QLatin1String("PyObject")) { s << INDENT << "// Check return type" << endl; s << INDENT; if (func->typeReplaced(0).isEmpty()) { - s << "PythonToCppFunc " PYTHON_TO_CPP_VAR " = " << cpythonIsConvertibleFunction(func->type()); - s << PYTHON_RETURN_VAR ");" << endl; - s << INDENT << "if (!" PYTHON_TO_CPP_VAR ") {" << endl; + s << "PythonToCppFunc " << PYTHON_TO_CPP_VAR << " = " << cpythonIsConvertibleFunction(func->type()); + s << PYTHON_RETURN_VAR << ");" << endl; + s << INDENT << "if (!" << PYTHON_TO_CPP_VAR << ") {" << endl; { Indentation indent(INDENT); s << INDENT << "Shiboken::warning(PyExc_RuntimeWarning, 2, "\ "\"Invalid return value in function %s, expected %s, got %s.\", \""; s << func->ownerClass()->name() << '.' << funcName << "\", " << getVirtualFunctionReturnTypeName(func); - s << ", Py_TYPE(" PYTHON_RETURN_VAR ")->tp_name);" << endl; - s << INDENT << "return " << defaultReturnExpr << ';' << endl; + s << ", Py_TYPE(" << PYTHON_RETURN_VAR << ")->tp_name);" << endl; + s << INDENT << returnStatement(defaultReturnExpr.returnValue()) << endl; } s << INDENT << '}' << endl; @@ -907,15 +963,16 @@ void CppGenerator::writeVirtualMethodNative(QTextStream&s, const AbstractMetaFun isNumber(func->type()->typeEntry()), func->typeReplaced(0)); s << ';' << endl; s << INDENT << "if (!typeIsValid"; - s << (isPointerToWrapperType(func->type()) ? " && " PYTHON_RETURN_VAR " != Py_None" : ""); + if (isPointerToWrapperType(func->type())) + s << " && " << PYTHON_RETURN_VAR << " != Py_None"; s << ") {" << endl; { Indentation indent(INDENT); s << INDENT << "Shiboken::warning(PyExc_RuntimeWarning, 2, "\ "\"Invalid return value in function %s, expected %s, got %s.\", \""; s << func->ownerClass()->name() << '.' << funcName << "\", " << getVirtualFunctionReturnTypeName(func); - s << ", Py_TYPE(" PYTHON_RETURN_VAR ")->tp_name);" << endl; - s << INDENT << "return " << defaultReturnExpr << ';' << endl; + s << ", Py_TYPE(" << PYTHON_RETURN_VAR << ")->tp_name);" << endl; + s << INDENT << returnStatement(defaultReturnExpr.returnValue()) << endl; } s << INDENT << '}' << endl; @@ -935,12 +992,12 @@ void CppGenerator::writeVirtualMethodNative(QTextStream&s, const AbstractMetaFun if (invalidateReturn) { s << INDENT << "if (invalidateArg0)" << endl; Indentation indentation(INDENT); - s << INDENT << "Shiboken::Object::releaseOwnership(" << PYTHON_RETURN_VAR ".object());" << endl; + s << INDENT << "Shiboken::Object::releaseOwnership(" << PYTHON_RETURN_VAR << ".object());" << endl; } for (int argIndex : qAsConst(invalidateArgs)) { s << INDENT << "if (invalidateArg" << argIndex << ')' << endl; Indentation indentation(INDENT); - s << INDENT << "Shiboken::Object::invalidate(PyTuple_GET_ITEM(" PYTHON_ARGS ", "; + s << INDENT << "Shiboken::Object::invalidate(PyTuple_GET_ITEM(" << PYTHON_ARGS << ", "; s << (argIndex - 1) << "));" << endl; } @@ -950,9 +1007,9 @@ void CppGenerator::writeVirtualMethodNative(QTextStream&s, const AbstractMetaFun for (const ArgumentModification &argMod : funcMod.argument_mods) { if (argMod.ownerships.contains(TypeSystem::NativeCode) && argMod.index == 0 && argMod.ownerships[TypeSystem::NativeCode] == TypeSystem::CppOwnership) { - s << INDENT << "if (Shiboken::Object::checkType(" PYTHON_RETURN_VAR "))" << endl; + s << INDENT << "if (Shiboken::Object::checkType(" << PYTHON_RETURN_VAR << "))" << endl; Indentation indent(INDENT); - s << INDENT << "Shiboken::Object::releaseOwnership(" PYTHON_RETURN_VAR ");" << endl; + s << INDENT << "Shiboken::Object::releaseOwnership(" << PYTHON_RETURN_VAR << ");" << endl; } } } @@ -978,7 +1035,7 @@ void CppGenerator::writeVirtualMethodNative(QTextStream&s, const AbstractMetaFun } if (func->type()->referenceType() == LValueReference && !isPointer(func->type())) s << '*'; - s << CPP_RETURN_VAR ";" << endl; + s << CPP_RETURN_VAR << ';' << endl; } s << '}' << endl << endl; @@ -995,7 +1052,7 @@ void CppGenerator::writeMetaObjectMethod(QTextStream& s, const AbstractMetaClass s << INDENT << "SbkObject* pySelf = Shiboken::BindingManager::instance().retrieveWrapper(this);" << endl; s << INDENT << "if (pySelf == NULL)" << endl; s << INDENT << INDENT << "return " << metaClass->qualifiedCppName() << "::metaObject();" << endl; - s << INDENT << "return PySide::SignalManager::retriveMetaObject(reinterpret_cast<PyObject*>(pySelf));" << endl; + s << INDENT << "return PySide::SignalManager::retrieveMetaObject(reinterpret_cast<PyObject*>(pySelf));" << endl; s << '}' << endl << endl; // qt_metacall function @@ -1259,7 +1316,7 @@ void CppGenerator::writeConverterFunctions(QTextStream &s, const AbstractMetaCla toCppConv = QLatin1Char('*') + cpythonWrapperCPtr(sourceClass->typeEntry(), QLatin1String("pyIn")); } else { // Constructor that does implicit conversion. - if (!conv->typeReplaced(1).isEmpty()) + if (!conv->typeReplaced(1).isEmpty() || conv->isModifiedToArray(1)) continue; const AbstractMetaType* sourceType = conv->arguments().constFirst()->type(); typeCheck = cpythonCheckFunction(sourceType); @@ -1419,7 +1476,7 @@ void CppGenerator::writeConverterRegister(QTextStream &s, const AbstractMetaClas sourceType = buildAbstractMetaTypeFromAbstractMetaClass(conv->ownerClass()); } else { // Constructor that does implicit conversion. - if (!conv->typeReplaced(1).isEmpty()) + if (!conv->typeReplaced(1).isEmpty() || conv->isModifiedToArray(1)) continue; sourceType = conv->arguments().constFirst()->type(); } @@ -1467,7 +1524,7 @@ void CppGenerator::writeMethodWrapperPreamble(QTextStream &s, OverloadData &over // Check if the right constructor was called. if (!ownerClass->hasPrivateDestructor()) { s << INDENT; - s << "if (Shiboken::Object::isUserType(" PYTHON_SELF_VAR ") && !Shiboken::ObjectType::canCallConstructor(" PYTHON_SELF_VAR "->ob_type, Shiboken::SbkType< ::"; + s << "if (Shiboken::Object::isUserType(self) && !Shiboken::ObjectType::canCallConstructor(self->ob_type, Shiboken::SbkType< ::"; QString qualifiedCppName; if (!context.forSmartPointer()) qualifiedCppName = ownerClass->qualifiedCppName(); @@ -1476,7 +1533,7 @@ void CppGenerator::writeMethodWrapperPreamble(QTextStream &s, OverloadData &over s << qualifiedCppName << " >()))" << endl; Indentation indent(INDENT); - s << INDENT << "return " << m_currentErrorCode << ';' << endl << endl; + s << INDENT << returnStatement(m_currentErrorCode) << endl << endl; } // Declare pointer for the underlying C++ object. s << INDENT << "::"; @@ -1497,7 +1554,7 @@ void CppGenerator::writeMethodWrapperPreamble(QTextStream &s, OverloadData &over writeCppSelfDefinition(s, rfunc, context, overloadData.hasStaticFunction()); } if (!rfunc->isInplaceOperator() && overloadData.hasNonVoidReturnType()) - s << INDENT << "PyObject* " PYTHON_RETURN_VAR " = 0;" << endl; + s << INDENT << "PyObject* " << PYTHON_RETURN_VAR << " = 0;" << endl; initPythonArguments = minArgs != maxArgs || maxArgs > 1; usesNamedArguments = rfunc->isCallOperator() || overloadData.hasArgumentWithDefaultValue(); @@ -1505,7 +1562,7 @@ void CppGenerator::writeMethodWrapperPreamble(QTextStream &s, OverloadData &over if (maxArgs > 0) { s << INDENT << "int overloadId = -1;" << endl; - s << INDENT << "PythonToCppFunc " PYTHON_TO_CPP_VAR; + s << INDENT << "PythonToCppFunc " << PYTHON_TO_CPP_VAR; if (pythonFunctionWrapperUsesListOfArguments(overloadData)) s << "[] = { 0" << QString::fromLatin1(", 0").repeated(maxArgs-1) << " }"; s << ';' << endl; @@ -1518,7 +1575,7 @@ void CppGenerator::writeMethodWrapperPreamble(QTextStream &s, OverloadData &over if (initPythonArguments) { s << INDENT << "int numArgs = "; if (minArgs == 0 && maxArgs == 1 && !rfunc->isConstructor() && !pythonFunctionWrapperUsesListOfArguments(overloadData)) - s << "(" PYTHON_ARG " == 0 ? 0 : 1);" << endl; + s << "(" << PYTHON_ARG << " == 0 ? 0 : 1);" << endl; else writeArgumentsInitializer(s, overloadData); } @@ -1534,7 +1591,7 @@ void CppGenerator::writeConstructorWrapper(QTextStream &s, const AbstractMetaFun const AbstractMetaClass* metaClass = rfunc->ownerClass(); s << "static int" << endl; - s << cpythonFunctionName(rfunc) << "(PyObject* " PYTHON_SELF_VAR ", PyObject* args, PyObject* kwds)" << endl; + s << cpythonFunctionName(rfunc) << "(PyObject* self, PyObject* args, PyObject* kwds)" << endl; s << '{' << endl; QSet<QString> argNamesSet; @@ -1560,10 +1617,10 @@ void CppGenerator::writeConstructorWrapper(QTextStream &s, const AbstractMetaFun s << INDENT << "const QMetaObject* metaObject;" << endl; } - s << INDENT << "SbkObject* sbkSelf = reinterpret_cast<SbkObject*>(" PYTHON_SELF_VAR ");" << endl; + s << INDENT << "SbkObject* sbkSelf = reinterpret_cast<SbkObject*>(self);" << endl; if (metaClass->isAbstract() || metaClass->baseClassNames().size() > 1) { - s << INDENT << "SbkObjectType* type = reinterpret_cast<SbkObjectType*>(" PYTHON_SELF_VAR "->ob_type);" << endl; + s << INDENT << "SbkObjectType* type = reinterpret_cast<SbkObjectType*>(self->ob_type);" << endl; s << INDENT << "SbkObjectType* myType = reinterpret_cast<SbkObjectType*>(" << cpythonTypeNameExt(metaClass->typeEntry()) << ");" << endl; } @@ -1577,7 +1634,7 @@ void CppGenerator::writeConstructorWrapper(QTextStream &s, const AbstractMetaFun s << INDENT << "\"'" << metaClass->qualifiedCppName(); } s << "' represents a C++ abstract class and cannot be instantiated\");" << endl; - s << INDENT << "return " << m_currentErrorCode << ';' << endl; + s << INDENT << returnStatement(m_currentErrorCode) << endl; } s << INDENT << '}' << endl << endl; } @@ -1608,7 +1665,7 @@ void CppGenerator::writeConstructorWrapper(QTextStream &s, const AbstractMetaFun { Indentation indent(INDENT); s << INDENT << "delete cptr;" << endl; - s << INDENT << "return " << m_currentErrorCode << ';' << endl; + s << INDENT << returnStatement(m_currentErrorCode) << endl; } s << INDENT << '}' << endl; if (overloadData.maxArgs() > 0) { @@ -1636,12 +1693,12 @@ void CppGenerator::writeConstructorWrapper(QTextStream &s, const AbstractMetaFun // Create metaObject and register signal/slot if (metaClass->isQObject() && usePySideExtensions()) { s << endl << INDENT << "// QObject setup" << endl; - s << INDENT << "PySide::Signal::updateSourceObject(" PYTHON_SELF_VAR ");" << endl; + s << INDENT << "PySide::Signal::updateSourceObject(self);" << endl; s << INDENT << "metaObject = cptr->metaObject(); // <- init python qt properties" << endl; - s << INDENT << "if (kwds && !PySide::fillQtProperties(" PYTHON_SELF_VAR ", metaObject, kwds, argNames, " << argNamesSet.count() << "))" << endl; + s << INDENT << "if (kwds && !PySide::fillQtProperties(self, metaObject, kwds, argNames, " << argNamesSet.count() << "))" << endl; { Indentation indentation(INDENT); - s << INDENT << "return " << m_currentErrorCode << ';' << endl; + s << INDENT << returnStatement(m_currentErrorCode) << endl; } } @@ -1694,7 +1751,7 @@ void CppGenerator::writeMethodWrapper(QTextStream &s, const AbstractMetaFunction int maxArgs = overloadData.maxArgs(); s << "static PyObject* "; - s << cpythonFunctionName(rfunc) << "(PyObject* " PYTHON_SELF_VAR; + s << cpythonFunctionName(rfunc) << "(PyObject* self"; if (maxArgs > 0) { s << ", PyObject* " << (pythonFunctionWrapperUsesListOfArguments(overloadData) ? "args" : PYTHON_ARG); if (overloadData.hasArgumentWithDefaultValue() || rfunc->isCallOperator()) @@ -1725,26 +1782,26 @@ void CppGenerator::writeMethodWrapper(QTextStream &s, const AbstractMetaFunction s << INDENT << "if (!isReverse" << endl; { Indentation indent(INDENT); - s << INDENT << "&& Shiboken::Object::checkType(" PYTHON_ARG ")" << endl; - s << INDENT << "&& !PyObject_TypeCheck(" PYTHON_ARG ", " PYTHON_SELF_VAR "->ob_type)" << endl; - s << INDENT << "&& PyObject_HasAttrString(" PYTHON_ARG ", const_cast<char*>(\"" << revOpName << "\"))) {" << endl; + s << INDENT << "&& Shiboken::Object::checkType(" << PYTHON_ARG << ")" << endl; + s << INDENT << "&& !PyObject_TypeCheck(" << PYTHON_ARG << ", self->ob_type)" << endl; + s << INDENT << "&& PyObject_HasAttrString(" << PYTHON_ARG << ", const_cast<char*>(\"" << revOpName << "\"))) {" << endl; // This PyObject_CallMethod call will emit lots of warnings like // "deprecated conversion from string constant to char *" during compilation // due to the method name argument being declared as "char*" instead of "const char*" // issue 6952 http://bugs.python.org/issue6952 - s << INDENT << "PyObject* revOpMethod = PyObject_GetAttrString(" PYTHON_ARG ", const_cast<char*>(\"" << revOpName << "\"));" << endl; + s << INDENT << "PyObject* revOpMethod = PyObject_GetAttrString(" << PYTHON_ARG << ", const_cast<char*>(\"" << revOpName << "\"));" << endl; s << INDENT << "if (revOpMethod && PyCallable_Check(revOpMethod)) {" << endl; { Indentation indent(INDENT); - s << INDENT << PYTHON_RETURN_VAR " = PyObject_CallFunction(revOpMethod, const_cast<char*>(\"O\"), " PYTHON_SELF_VAR ");" << endl; + s << INDENT << PYTHON_RETURN_VAR << " = PyObject_CallFunction(revOpMethod, const_cast<char*>(\"O\"), self);" << endl; s << INDENT << "if (PyErr_Occurred() && (PyErr_ExceptionMatches(PyExc_NotImplementedError)"; s << " || PyErr_ExceptionMatches(PyExc_AttributeError))) {" << endl; { Indentation indent(INDENT); s << INDENT << "PyErr_Clear();" << endl; - s << INDENT << "Py_XDECREF(" PYTHON_RETURN_VAR ");" << endl; - s << INDENT << PYTHON_RETURN_VAR " = 0;" << endl; + s << INDENT << "Py_XDECREF(" << PYTHON_RETURN_VAR << ");" << endl; + s << INDENT << PYTHON_RETURN_VAR << " = 0;" << endl; } s << INDENT << '}' << endl; } @@ -1754,7 +1811,7 @@ void CppGenerator::writeMethodWrapper(QTextStream &s, const AbstractMetaFunction s << INDENT << "}" << endl; } s << INDENT << "// Do not enter here if other object has implemented a reverse operator." << endl; - s << INDENT << "if (!" PYTHON_RETURN_VAR ") {" << endl << endl; + s << INDENT << "if (!" << PYTHON_RETURN_VAR << ") {" << endl << endl; } if (maxArgs > 0) @@ -1763,7 +1820,7 @@ void CppGenerator::writeMethodWrapper(QTextStream &s, const AbstractMetaFunction writeFunctionCalls(s, overloadData, classContext); if (callExtendedReverseOperator) - s << endl << INDENT << "} // End of \"if (!" PYTHON_RETURN_VAR ")\"" << endl; + s << endl << INDENT << "} // End of \"if (!" << PYTHON_RETURN_VAR << ")\"" << endl; s << endl; @@ -1771,10 +1828,10 @@ void CppGenerator::writeMethodWrapper(QTextStream &s, const AbstractMetaFunction if (hasReturnValue) { if (rfunc->isInplaceOperator()) { - s << INDENT << "Py_INCREF(" PYTHON_SELF_VAR ");\n"; - s << INDENT << "return " PYTHON_SELF_VAR ";\n"; + s << INDENT << "Py_INCREF(self);\n"; + s << INDENT << "return self;\n"; } else { - s << INDENT << "return " PYTHON_RETURN_VAR ";\n"; + s << INDENT << "return " << PYTHON_RETURN_VAR << ";\n"; } } else { s << INDENT << "Py_RETURN_NONE;" << endl; @@ -1795,7 +1852,7 @@ void CppGenerator::writeArgumentsInitializer(QTextStream& s, OverloadData& overl int maxArgs = overloadData.maxArgs(); s << INDENT << "PyObject* "; - s << PYTHON_ARGS "[] = {" + s << PYTHON_ARGS << "[] = {" << QString(maxArgs, QLatin1Char('0')).split(QLatin1String(""), QString::SkipEmptyParts).join(QLatin1String(", ")) << "};" << endl; s << endl; @@ -1807,8 +1864,8 @@ void CppGenerator::writeArgumentsInitializer(QTextStream& s, OverloadData& overl s << INDENT << "PyObject* nonvarargs = PyTuple_GetSlice(args, 0, " << maxArgs << ");" << endl; s << INDENT << "Shiboken::AutoDecRef auto_nonvarargs(nonvarargs);" << endl; - s << INDENT << PYTHON_ARGS "[" << maxArgs << "] = PyTuple_GetSlice(args, " << maxArgs << ", numArgs);" << endl; - s << INDENT << "Shiboken::AutoDecRef auto_varargs(" PYTHON_ARGS "[" << maxArgs << "]);" << endl; + s << INDENT << PYTHON_ARGS << '[' << maxArgs << "] = PyTuple_GetSlice(args, " << maxArgs << ", numArgs);" << endl; + s << INDENT << "Shiboken::AutoDecRef auto_varargs(" << PYTHON_ARGS << "[" << maxArgs << "]);" << endl; s << endl; } @@ -1822,7 +1879,7 @@ void CppGenerator::writeArgumentsInitializer(QTextStream& s, OverloadData& overl { Indentation indent(INDENT); s << INDENT << "PyErr_SetString(PyExc_TypeError, \"" << fullPythonFunctionName(rfunc) << "(): too many arguments\");" << endl; - s << INDENT << "return " << m_currentErrorCode << ';' << endl; + s << INDENT << returnStatement(m_currentErrorCode) << endl; } s << INDENT << '}'; } @@ -1835,7 +1892,7 @@ void CppGenerator::writeArgumentsInitializer(QTextStream& s, OverloadData& overl { Indentation indent(INDENT); s << INDENT << "PyErr_SetString(PyExc_TypeError, \"" << fullPythonFunctionName(rfunc) << "(): not enough arguments\");" << endl; - s << INDENT << "return " << m_currentErrorCode << ';' << endl; + s << INDENT << returnStatement(m_currentErrorCode) << endl; } s << INDENT << '}'; } @@ -1867,13 +1924,12 @@ void CppGenerator::writeArgumentsInitializer(QTextStream& s, OverloadData& overl s << "PyArg_ParseTuple(" << argsVar << ", \"|" << QByteArray(maxArgs, 'O') << ':' << funcName << '"'; else s << "PyArg_UnpackTuple(" << argsVar << ", \"" << funcName << "\", " << minArgs << ", " << maxArgs; - QStringList palist; for (int i = 0; i < maxArgs; i++) - palist << QString::fromLatin1("&(" PYTHON_ARGS "[%1])").arg(i); - s << ", " << palist.join(QLatin1String(", ")) << "))" << endl; + s << ", &(" << PYTHON_ARGS << '[' << i << "])"; + s << "))" << endl; { Indentation indent(INDENT); - s << INDENT << "return " << m_currentErrorCode << ';' << endl; + s << INDENT << returnStatement(m_currentErrorCode) << endl; } s << endl; } @@ -1895,7 +1951,7 @@ void CppGenerator::writeCppSelfDefinition(QTextStream &s, } QString cppSelfAttribution; - QString pythonSelfVar = QLatin1String(PYTHON_SELF_VAR); + QString pythonSelfVar = QLatin1String("self"); QString cpythonWrapperCPtrResult; if (!context.forSmartPointer()) cpythonWrapperCPtrResult = cpythonWrapperCPtr(metaClass, pythonSelfVar); @@ -1908,7 +1964,7 @@ void CppGenerator::writeCppSelfDefinition(QTextStream &s, .arg(className, QLatin1String(CPP_SELF_VAR), cast, cpythonWrapperCPtrResult); } else { - s << INDENT << className << "* " CPP_SELF_VAR " = 0;" << endl; + s << INDENT << className << "* " << CPP_SELF_VAR << " = 0;" << endl; writeUnusedVariableCast(s, QLatin1String(CPP_SELF_VAR)); cppSelfAttribution = QString::fromLatin1("%1 = %2%3") .arg(QLatin1String(CPP_SELF_VAR), @@ -1918,17 +1974,17 @@ void CppGenerator::writeCppSelfDefinition(QTextStream &s, // Checks if the underlying C++ object is valid. if (hasStaticOverload && !cppSelfAsReference) { - s << INDENT << "if (" PYTHON_SELF_VAR ") {" << endl; + s << INDENT << "if (self) {" << endl; { Indentation indent(INDENT); - writeInvalidPyObjectCheck(s, QLatin1String(PYTHON_SELF_VAR)); + writeInvalidPyObjectCheck(s, QLatin1String("self")); s << INDENT << cppSelfAttribution << ';' << endl; } s << INDENT << '}' << endl; return; } - writeInvalidPyObjectCheck(s, QLatin1String(PYTHON_SELF_VAR)); + writeInvalidPyObjectCheck(s, QLatin1String("self")); s << INDENT << cppSelfAttribution << ';' << endl; } @@ -1942,17 +1998,17 @@ void CppGenerator::writeCppSelfDefinition(QTextStream &s, if (func->isOperatorOverload() && func->isBinaryOperator()) { QString checkFunc = cpythonCheckFunction(func->ownerClass()->typeEntry()); - s << INDENT << "bool isReverse = " << checkFunc << PYTHON_ARG ")" << endl; + s << INDENT << "bool isReverse = " << checkFunc << PYTHON_ARG << ')' << endl; { Indentation indent1(INDENT); Indentation indent2(INDENT); Indentation indent3(INDENT); Indentation indent4(INDENT); - s << INDENT << "&& !" << checkFunc << PYTHON_SELF_VAR ");" << endl; + s << INDENT << "&& !" << checkFunc << "self);" << endl; } s << INDENT << "if (isReverse)" << endl; Indentation indent(INDENT); - s << INDENT << "std::swap(" PYTHON_SELF_VAR ", " PYTHON_ARG ");" << endl; + s << INDENT << "std::swap(self, " << PYTHON_ARG << ");" << endl; } writeCppSelfDefinition(s, context, hasStaticOverload); @@ -2053,17 +2109,20 @@ void CppGenerator::writeErrorSection(QTextStream& s, OverloadData& overloadData) << ", 0};" << endl; s << INDENT << "Shiboken::setErrorAboutWrongArguments(" << argsVar << ", \"" << funcName << "\", overloads);" << endl; } - s << INDENT << "return " << m_currentErrorCode << ';' << endl; + s << INDENT << returnStatement(m_currentErrorCode) << endl; } void CppGenerator::writeFunctionReturnErrorCheckSection(QTextStream& s, bool hasReturnValue) { - s << INDENT << "if (PyErr_Occurred()" << (hasReturnValue ? " || !" PYTHON_RETURN_VAR : "") << ") {" << endl; + s << INDENT << "if (PyErr_Occurred()"; + if (hasReturnValue) + s << " || !" << PYTHON_RETURN_VAR; + s << ") {" << endl; { Indentation indent(INDENT); if (hasReturnValue) - s << INDENT << "Py_XDECREF(" PYTHON_RETURN_VAR ");" << endl; - s << INDENT << "return " << m_currentErrorCode << ';' << endl; + s << INDENT << "Py_XDECREF(" << PYTHON_RETURN_VAR << ");" << endl; + s << INDENT << returnStatement(m_currentErrorCode) << endl; } s << INDENT << '}' << endl; } @@ -2072,12 +2131,13 @@ void CppGenerator::writeInvalidPyObjectCheck(QTextStream& s, const QString& pyOb { s << INDENT << "if (!Shiboken::Object::isValid(" << pyObj << "))" << endl; Indentation indent(INDENT); - s << INDENT << "return " << m_currentErrorCode << ';' << endl; + s << INDENT << returnStatement(m_currentErrorCode) << endl; } static QString pythonToCppConverterForArgumentName(const QString& argumentName) { - static const QRegularExpression pyArgsRegex(QLatin1String(PYTHON_ARGS"(\\[\\d+[-]?\\d*\\])")); + static const QRegularExpression pyArgsRegex(QLatin1String(PYTHON_ARGS) + + QLatin1String(R"((\[\d+[-]?\d*\]))")); Q_ASSERT(pyArgsRegex.isValid()); const QRegularExpressionMatch match = pyArgsRegex.match(argumentName); QString result = QLatin1String(PYTHON_TO_CPP_VAR); @@ -2379,7 +2439,7 @@ void CppGenerator::writeConversionRule(QTextStream& s, const AbstractMetaFunctio void CppGenerator::writeNoneReturn(QTextStream& s, const AbstractMetaFunction* func, bool thereIsReturnValue) { if (thereIsReturnValue && (!func->type() || func->argumentRemoved(0)) && !injectedCodeHasReturnValueAttribution(func)) { - s << INDENT << PYTHON_RETURN_VAR " = Py_None;" << endl; + s << INDENT << PYTHON_RETURN_VAR << " = Py_None;" << endl; s << INDENT << "Py_INCREF(Py_None);" << endl; } } @@ -2493,8 +2553,9 @@ void CppGenerator::writeOverloadedFunctionDecisorEngine(QTextStream& s, const Ov const AbstractMetaFunction* refFunc = overloadData->referenceFunction(); QStringList typeChecks; + QString pyArgName = (usePyArgs && maxArgs > 1) - ? QString::fromLatin1(PYTHON_ARGS "[%1]").arg(overloadData->argPos()) + ? pythonArgsAt(overloadData->argPos()) : QLatin1String(PYTHON_ARG); OverloadData* od = overloadData; int startArg = od->argPos(); @@ -2503,7 +2564,7 @@ void CppGenerator::writeOverloadedFunctionDecisorEngine(QTextStream& s, const Ov bool typeReplacedByPyObject = od->argumentTypeReplaced() == QLatin1String("PyObject"); if (!typeReplacedByPyObject) { if (usePyArgs) - pyArgName = QString::fromLatin1(PYTHON_ARGS "[%1]").arg(od->argPos()); + pyArgName = pythonArgsAt(od->argPos()); QString typeCheck; QTextStream tck(&typeCheck); const AbstractMetaFunction* func = od->referenceFunction(); @@ -2613,7 +2674,7 @@ void CppGenerator::writeSingleFunctionCall(QTextStream &s, s << INDENT << "PyErr_Format(PyExc_TypeError, \"%s is a private method.\", \"" << func->signature().replace(QLatin1String("::"), QLatin1String(".")) << "\");" << endl; - s << INDENT << "return " << m_currentErrorCode << ';' << endl; + s << INDENT << returnStatement(m_currentErrorCode) << endl; return; } @@ -2630,7 +2691,8 @@ void CppGenerator::writeSingleFunctionCall(QTextStream &s, const AbstractMetaArgument* arg = func->arguments().at(argIdx); if (func->argumentRemoved(argIdx + 1)) { if (!arg->defaultValueExpression().isEmpty()) { - QString cppArgRemoved = QString::fromLatin1(CPP_ARG_REMOVED "%1").arg(argIdx); + const QString cppArgRemoved = QLatin1String(CPP_ARG_REMOVED) + + QString::number(argIdx); s << INDENT << getFullTypeName(arg->type()) << ' ' << cppArgRemoved; s << " = " << guessScopeForDefaultValue(func, arg) << ';' << endl; writeUnusedVariableCast(s, cppArgRemoved); @@ -2649,8 +2711,8 @@ void CppGenerator::writeSingleFunctionCall(QTextStream &s, if (!argType || (mayHaveUnunsedArguments && !injectedCodeUsesArgument(func, argIdx))) continue; int argPos = argIdx - removedArgs; - QString argName = QString::fromLatin1(CPP_ARG"%1").arg(argPos); - QString pyArgName = usePyArgs ? QString::fromLatin1(PYTHON_ARGS "[%1]").arg(argPos) : QLatin1String(PYTHON_ARG); + QString argName = QLatin1String(CPP_ARG) + QString::number(argPos); + QString pyArgName = usePyArgs ? pythonArgsAt(argPos) : QLatin1String(PYTHON_ARG); QString defaultValue = guessScopeForDefaultValue(func, arg); writeArgumentConversion(s, argType, argName, pyArgName, func->implementingClass(), defaultValue, func->isUserAdded()); } @@ -2896,10 +2958,8 @@ void CppGenerator::writePythonToCppConversionFunctions(QTextStream& s, const Abs const AbstractMetaType* type = containerType->instantiations().at(i); QString typeName = getFullTypeName(type); if (type->isValue() && isValueTypeWithCopyConstructorOnly(type)) { - static const QRegularExpression regex(QLatin1String(CONVERTTOCPP_REGEX)); - Q_ASSERT(regex.isValid()); for (int pos = 0; ; ) { - const QRegularExpressionMatch match = regex.match(code, pos); + const QRegularExpressionMatch match = convertToCppRegEx().match(code, pos); if (!match.hasMatch()) break; pos = match.capturedEnd(); @@ -2954,15 +3014,13 @@ void CppGenerator::writeNamedArgumentResolution(QTextStream& s, const AbstractMe s << INDENT << "PyObject* "; for (const AbstractMetaArgument *arg : args) { int pyArgIndex = arg->argumentIndex() - OverloadData::numberOfRemovedArguments(func, arg->argumentIndex()); - QString pyArgName = usePyArgs - ? QString::fromLatin1(PYTHON_ARGS "[%1]").arg(pyArgIndex) - : QLatin1String(PYTHON_ARG); + QString pyArgName = usePyArgs ? pythonArgsAt(pyArgIndex) : QLatin1String(PYTHON_ARG); s << "value = PyDict_GetItemString(kwds, \"" << arg->name() << "\");" << endl; s << INDENT << "if (value && " << pyArgName << ") {" << endl; { Indentation indent(INDENT); s << INDENT << pyErrString.arg(arg->name()) << endl; - s << INDENT << "return " << m_currentErrorCode << ';' << endl; + s << INDENT << returnStatement(m_currentErrorCode) << endl; } s << INDENT << "} else if (value) {" << endl; { @@ -2989,7 +3047,7 @@ QString CppGenerator::argumentNameFromIndex(const AbstractMetaFunction* func, in *wrappedClass = 0; QString pyArgName; if (argIndex == -1) { - pyArgName = QLatin1String(PYTHON_SELF_VAR); + pyArgName = QLatin1String("self"); *wrappedClass = func->implementingClass(); } else if (argIndex == 0) { AbstractMetaType *funcType = func->type(); @@ -3016,12 +3074,23 @@ QString CppGenerator::argumentNameFromIndex(const AbstractMetaFunction* func, in && OverloadData::isSingleArgument(getFunctionGroups(func->implementingClass())[func->name()])) pyArgName = QLatin1String(PYTHON_ARG); else - pyArgName = QString::fromLatin1(PYTHON_ARGS "[%1]").arg(argIndex - 1); + pyArgName = pythonArgsAt(argIndex - 1); } } return pyArgName; } +static QStringList defaultExceptionHandling() +{ + static const QStringList result{ + QLatin1String("} catch (const std::exception &e) {"), + QLatin1String(" PyErr_SetString(PyExc_RuntimeError, e.what());"), + QLatin1String("} catch (...) {"), + QLatin1String(" PyErr_SetString(PyExc_RuntimeError, \"An unknown exception was caught\");"), + QLatin1String("}")}; + return result; +} + void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *func, GeneratorContext &context, int maxArgs) { @@ -3037,12 +3106,12 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f } if (func->isAbstract()) { - s << INDENT << "if (Shiboken::Object::hasCppWrapper(reinterpret_cast<SbkObject*>(" PYTHON_SELF_VAR "))) {\n"; + s << INDENT << "if (Shiboken::Object::hasCppWrapper(reinterpret_cast<SbkObject*>(self))) {\n"; { Indentation indent(INDENT); s << INDENT << "PyErr_SetString(PyExc_NotImplementedError, \"pure virtual method '"; s << func->ownerClass()->name() << '.' << func->name() << "()' not implemented.\");" << endl; - s << INDENT << "return " << m_currentErrorCode << ';' << endl; + s << INDENT << returnStatement(m_currentErrorCode) << endl; } s << INDENT << "}\n"; } @@ -3091,16 +3160,21 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f if (hasConversionRule) userArgs << arg->name() + QLatin1String(CONV_RULE_OUT_VAR_SUFFIX); else if (!arg->defaultValueExpression().isEmpty()) - userArgs << QString::fromLatin1(CPP_ARG_REMOVED "%1").arg(i); + userArgs.append(QLatin1String(CPP_ARG_REMOVED) + QString::number(i)); } else { int idx = arg->argumentIndex() - removedArgs; bool deRef = isValueTypeWithCopyConstructorOnly(arg->type()) || isObjectTypeUsedAsValueType(arg->type()) || (arg->type()->referenceType() == LValueReference && isWrapperType(arg->type()) && !isPointer(arg->type())); - QString argName = hasConversionRule - ? arg->name() + QLatin1String(CONV_RULE_OUT_VAR_SUFFIX) - : QString::fromLatin1("%1" CPP_ARG "%2").arg(deRef ? QLatin1String("*") : QString()).arg(idx); - userArgs << argName; + if (hasConversionRule) { + userArgs.append(arg->name() + QLatin1String(CONV_RULE_OUT_VAR_SUFFIX)); + } else { + QString argName; + if (deRef) + argName += QLatin1Char('*'); + argName += QLatin1String(CPP_ARG) + QString::number(idx); + userArgs.append(argName); + } } } @@ -3113,17 +3187,16 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f bool argsClear = true; for (int i = func->arguments().size() - 1; i >= maxArgs + removedArgs; i--) { const AbstractMetaArgument* arg = func->arguments().at(i); - bool defValModified = arg->defaultValueExpression() != arg->originalDefaultValueExpression(); + const bool defValModified = arg->hasModifiedDefaultValueExpression(); bool hasConversionRule = !func->conversionRule(TypeSystem::NativeCode, arg->argumentIndex() + 1).isEmpty(); if (argsClear && !defValModified && !hasConversionRule) continue; - else - argsClear = false; + argsClear = false; otherArgsModified |= defValModified || hasConversionRule || func->argumentRemoved(i + 1); if (hasConversionRule) otherArgs.prepend(arg->name() + QLatin1String(CONV_RULE_OUT_VAR_SUFFIX)); else - otherArgs.prepend(QString::fromLatin1(CPP_ARG_REMOVED "%1").arg(i)); + otherArgs.prepend(QLatin1String(CPP_ARG_REMOVED) + QString::number(i)); } if (otherArgsModified) userArgs << otherArgs; @@ -3135,10 +3208,11 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f QString useVAddr; QTextStream uva(&useVAddr); if (func->isOperatorOverload() && !func->isCallOperator()) { - QString firstArg = QLatin1String("(*" CPP_SELF_VAR ")"); - if (func->isPointerOperator()) - firstArg.remove(1, 1); // remove the de-reference operator - + QString firstArg(QLatin1Char('(')); + if (!func->isPointerOperator()) // no de-reference operator + firstArg += QLatin1Char('*'); + firstArg += QLatin1String(CPP_SELF_VAR); + firstArg += QLatin1Char(')'); QString secondArg = QLatin1String(CPP_ARG0); if (!func->isUnaryOperator() && shouldDereferenceArgumentPointer(func->arguments().constFirst())) { secondArg.prepend(QLatin1String("(*")); @@ -3211,7 +3285,8 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f } else { const QString selfVarCast = func->ownerClass() == func->implementingClass() ? QLatin1String(CPP_SELF_VAR) - : QLatin1String("reinterpret_cast<") + methodCallClassName + QLatin1String(" *>(" CPP_SELF_VAR ")"); + : QLatin1String("reinterpret_cast<") + methodCallClassName + + QLatin1String(" *>(") + QLatin1String(CPP_SELF_VAR) + QLatin1Char(')'); if (func->isConstant()) { if (avoidProtectedHack()) { mc << "const_cast<const ::"; @@ -3219,7 +3294,8 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f // PYSIDE-500: Need a special wrapper cast when inherited const QString selfWrapCast = func->ownerClass() == func->implementingClass() ? QLatin1String(CPP_SELF_VAR) - : QLatin1String("reinterpret_cast<") + wrapperName(func->ownerClass()) + QLatin1String(" *>(" CPP_SELF_VAR ")"); + : QLatin1String("reinterpret_cast<") + wrapperName(func->ownerClass()) + + QLatin1String(" *>(") + QLatin1String(CPP_SELF_VAR) + QLatin1Char(')'); mc << wrapperName(func->ownerClass()); mc << "*>(" << selfWrapCast << ")->"; } @@ -3263,7 +3339,7 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f methodCallClassName); normalCall.remove(QLatin1String("::%CLASS_NAME::")); methodCall.clear(); - mc << "Shiboken::Object::hasCppWrapper(reinterpret_cast<SbkObject*>(" PYTHON_SELF_VAR ")) ? "; + mc << "Shiboken::Object::hasCppWrapper(reinterpret_cast<SbkObject*>(self)) ? "; mc << virtualCall << " : " << normalCall; } } @@ -3271,7 +3347,19 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f } if (!injectedCodeCallsCppFunction(func)) { - s << INDENT << BEGIN_ALLOW_THREADS << endl << INDENT; + const bool allowThread = func->allowThread(); + const bool generateExceptionHandling = func->generateExceptionHandling(); + if (generateExceptionHandling) { + s << INDENT << "try {\n"; + ++INDENT.indent; + if (allowThread) { + s << INDENT << "Shiboken::ThreadStateSaver threadSaver;\n" + << INDENT << "threadSaver.save();\n"; + } + } else if (allowThread) { + s << INDENT << BEGIN_ALLOW_THREADS << endl; + } + s << INDENT; if (isCtor) { s << (useVAddr.isEmpty() ? QString::fromLatin1("cptr = %1;").arg(methodCall) : useVAddr) << endl; @@ -3299,18 +3387,23 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f methodCall.append(QLatin1Char(')')); } } - s << " " CPP_RETURN_VAR " = "; + s << " " << CPP_RETURN_VAR << " = "; s << methodCall << ';' << endl; } else { s << methodCall << ';' << endl; } - s << INDENT << END_ALLOW_THREADS << endl; + if (allowThread) { + s << INDENT << (generateExceptionHandling + ? "threadSaver.restore();" : END_ALLOW_THREADS) << '\n'; + } + + // Convert result if (!func->conversionRule(TypeSystem::TargetLangCode, 0).isEmpty()) { writeConversionRule(s, func, TypeSystem::TargetLangCode, QLatin1String(PYTHON_RETURN_VAR)); } else if (!isCtor && !func->isInplaceOperator() && func->type() && !injectedCodeHasReturnValueAttribution(func, TypeSystem::TargetLangCode)) { - s << INDENT << PYTHON_RETURN_VAR " = "; + s << INDENT << PYTHON_RETURN_VAR << " = "; if (isObjectTypeUsedAsValueType(func->type())) { s << "Shiboken::Object::newObject(reinterpret_cast<SbkObjectType *>(" << cpythonTypeNameExt(func->type()->typeEntry()) << "), " << CPP_RETURN_VAR << ", true, true)"; @@ -3319,6 +3412,13 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f } s << ';' << endl; } + + if (generateExceptionHandling) { // "catch" code + --INDENT.indent; + const QStringList handlingCode = defaultExceptionHandling(); + for (const auto &line : handlingCode) + s << INDENT << line << '\n'; + } } } @@ -3370,7 +3470,7 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f s << "getOwnership(" << pyArgName << ");"; } else if (wrappedClass->hasVirtualDestructor()) { if (arg_mod.index == 0) - s << "releaseOwnership(" PYTHON_RETURN_VAR ");"; + s << "releaseOwnership(" << PYTHON_RETURN_VAR << ");"; else s << "releaseOwnership(" << pyArgName << ");"; } else { @@ -3406,10 +3506,10 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f else s << INDENT << "Shiboken::Object::removeReference("; - s << "reinterpret_cast<SbkObject*>(" PYTHON_SELF_VAR "), \""; + s << "reinterpret_cast<SbkObject*>(self), \""; QString varName = arg_mod.referenceCounts.constFirst().varName; if (varName.isEmpty()) - varName = func->minimalSignature() + QString().number(arg_mod.index); + varName = func->minimalSignature() + QString::number(arg_mod.index); s << varName << "\", " << pyArgName << (refCount.action == ReferenceCount::Add ? ", true" : "") @@ -3531,7 +3631,7 @@ void CppGenerator::writeEnumConverterInitialization(QTextStream& s, const TypeEn const FlagsTypeEntry* flags = 0; if (enumType->isFlags()) - flags = reinterpret_cast<const FlagsTypeEntry*>(enumType); + flags = static_cast<const FlagsTypeEntry*>(enumType); s << INDENT << "// Register converter for " << enumFlagName << " '" << enumType->qualifiedCppName() << "'." << endl; s << INDENT << '{' << endl; @@ -3575,7 +3675,7 @@ void CppGenerator::writeEnumConverterInitialization(QTextStream& s, const TypeEn s << INDENT << '}' << endl; if (!flags) - writeEnumConverterInitialization(s, reinterpret_cast<const EnumTypeEntry*>(enumType)->flags()); + writeEnumConverterInitialization(s, static_cast<const EnumTypeEntry*>(enumType)->flags()); } void CppGenerator::writeContainerConverterInitialization(QTextStream& s, const AbstractMetaType* type) @@ -3656,10 +3756,7 @@ bool CppGenerator::supportsSequenceProtocol(const AbstractMetaClass* metaClass) } const ComplexTypeEntry* baseType = metaClass->typeEntry()->baseContainerType(); - if (baseType && baseType->isContainer()) - return true; - - return false; + return baseType && baseType->isContainer(); } bool CppGenerator::shouldGenerateGetSetList(const AbstractMetaClass* metaClass) @@ -3872,7 +3969,7 @@ void CppGenerator::writeMappingMethods(QTextStream &s, CodeSnipList snips = func->injectedCodeSnips(TypeSystem::CodeSnipPositionAny, TypeSystem::TargetLangCode); s << funcRetVal << ' ' << funcName << '(' << funcArgs << ')' << endl << '{' << endl; - writeInvalidPyObjectCheck(s, QLatin1String(PYTHON_SELF_VAR)); + writeInvalidPyObjectCheck(s, QLatin1String("self")); writeCppSelfDefinition(s, func, context); @@ -3899,7 +3996,7 @@ void CppGenerator::writeSequenceMethods(QTextStream &s, CodeSnipList snips = func->injectedCodeSnips(TypeSystem::CodeSnipPositionAny, TypeSystem::TargetLangCode); s << funcRetVal << ' ' << funcName << '(' << funcArgs << ')' << endl << '{' << endl; - writeInvalidPyObjectCheck(s, QLatin1String(PYTHON_SELF_VAR)); + writeInvalidPyObjectCheck(s, QLatin1String("self")); writeCppSelfDefinition(s, func, context); @@ -3964,7 +4061,6 @@ void CppGenerator::writeTypeAsMappingDefinition(QTextStream& s, const AbstractMe funcs.insert(QLatin1String("__msetitem__"), QString()); } - QString baseName = cpythonBaseName(metaClass); for (auto it = m_mpFuncs.cbegin(), end = m_mpFuncs.cend(); it != end; ++it) { const QString &mpName = it.key(); if (funcs[mpName].isEmpty()) @@ -4015,7 +4111,8 @@ void CppGenerator::writeTypeAsNumberDefinition(QTextStream& s, const AbstractMet QString baseName = cpythonBaseName(metaClass); - nb[QLatin1String("bool")] = hasBoolCast(metaClass) ? baseName + QLatin1String("___nb_bool") : QString(); + if (hasBoolCast(metaClass)) + nb.insert(QLatin1String("bool"), baseName + QLatin1String("___nb_bool")); for (QHash<QString, QString>::const_iterator it = m_nbFuncs.cbegin(), end = m_nbFuncs.cend(); it != end; ++it) { const QString &nbName = it.key(); @@ -4054,9 +4151,9 @@ void CppGenerator::writeTpTraverseFunction(QTextStream& s, const AbstractMetaCla { QString baseName = cpythonBaseName(metaClass); s << "static int "; - s << baseName << "_traverse(PyObject* " PYTHON_SELF_VAR ", visitproc visit, void* arg)" << endl; + s << baseName << "_traverse(PyObject* self, visitproc visit, void* arg)" << endl; s << '{' << endl; - s << INDENT << "return reinterpret_cast<PyTypeObject *>(SbkObject_TypeF())->tp_traverse(" PYTHON_SELF_VAR ", visit, arg);" << endl; + s << INDENT << "return reinterpret_cast<PyTypeObject *>(SbkObject_TypeF())->tp_traverse(self, visit, arg);" << endl; s << '}' << endl; } @@ -4064,9 +4161,9 @@ void CppGenerator::writeTpClearFunction(QTextStream& s, const AbstractMetaClass* { QString baseName = cpythonBaseName(metaClass); s << "static int "; - s << baseName << "_clear(PyObject* " PYTHON_SELF_VAR ")" << endl; + s << baseName << "_clear(PyObject* self)" << endl; s << '{' << endl; - s << INDENT << "return reinterpret_cast<PyTypeObject *>(SbkObject_TypeF())->tp_clear(" PYTHON_SELF_VAR ");" << endl; + s << INDENT << "return reinterpret_cast<PyTypeObject *>(SbkObject_TypeF())->tp_clear(self);" << endl; s << '}' << endl; } @@ -4074,7 +4171,7 @@ void CppGenerator::writeCopyFunction(QTextStream &s, GeneratorContext &context) { const AbstractMetaClass *metaClass = context.metaClass(); const QString className = chopType(cpythonTypeName(metaClass)); - s << "static PyObject* " << className << "___copy__(PyObject* " PYTHON_SELF_VAR ")" << endl; + s << "static PyObject* " << className << "___copy__(PyObject* self)" << endl; s << "{" << endl; writeCppSelfDefinition(s, context, false, true); QString conversionCode; @@ -4084,9 +4181,9 @@ void CppGenerator::writeCopyFunction(QTextStream &s, GeneratorContext &context) conversionCode = cpythonToPythonConversionFunction(context.preciseType()); s << INDENT << "PyObject* " << PYTHON_RETURN_VAR << " = " << conversionCode; - s << CPP_SELF_VAR ");" << endl; + s << CPP_SELF_VAR << ");" << endl; writeFunctionReturnErrorCheckSection(s); - s << INDENT << "return " PYTHON_RETURN_VAR ";" << endl; + s << INDENT << "return " << PYTHON_RETURN_VAR << ";" << endl; s << "}" << endl; s << endl; } @@ -4096,7 +4193,7 @@ void CppGenerator::writeGetterFunction(QTextStream &s, GeneratorContext &context) { ErrorCode errorCode(0); - s << "static PyObject* " << cpythonGetterFunctionName(metaField) << "(PyObject* " PYTHON_SELF_VAR ", void*)" << endl; + s << "static PyObject* " << cpythonGetterFunctionName(metaField) << "(PyObject* self, void*)" << endl; s << '{' << endl; writeCppSelfDefinition(s, context); @@ -4161,7 +4258,7 @@ void CppGenerator::writeGetterFunction(QTextStream &s, s << INDENT << "pyOut = "; s << "Shiboken::Object::newObject(reinterpret_cast<SbkObjectType *>(" << cpythonTypeNameExt(fieldType) << "), " << cppField << ", false, true);" << endl; - s << INDENT << "Shiboken::Object::setParent(" PYTHON_SELF_VAR ", pyOut)"; + s << INDENT << "Shiboken::Object::setParent(self, pyOut)"; } else { s << INDENT << "pyOut = "; writeToPythonConversion(s, fieldType, metaField->enclosingClass(), cppField); @@ -4177,7 +4274,7 @@ void CppGenerator::writeSetterFunction(QTextStream &s, GeneratorContext &context) { ErrorCode errorCode(0); - s << "static int " << cpythonSetterFunctionName(metaField) << "(PyObject* " PYTHON_SELF_VAR ", PyObject* pyIn, void*)" << endl; + s << "static int " << cpythonSetterFunctionName(metaField) << "(PyObject* self, PyObject* pyIn, void*)" << endl; s << '{' << endl; writeCppSelfDefinition(s, context); @@ -4193,7 +4290,7 @@ void CppGenerator::writeSetterFunction(QTextStream &s, AbstractMetaType* fieldType = metaField->type(); - s << INDENT << "PythonToCppFunc " << PYTHON_TO_CPP_VAR << ';' << endl; + s << INDENT << "PythonToCppFunc " << PYTHON_TO_CPP_VAR << "{nullptr};" << endl; s << INDENT << "if (!"; writeTypeCheck(s, fieldType, QLatin1String("pyIn"), isNumber(fieldType->typeEntry())); s << ") {" << endl; @@ -4219,6 +4316,8 @@ void CppGenerator::writeSetterFunction(QTextStream &s, s << INDENT << PYTHON_TO_CPP_VAR << "(pyIn, &cppOut_local);" << endl; s << INDENT << cppField << " = cppOut_local"; } else { + if (isPointerToConst(fieldType)) + s << "const "; s << getFullTypeNameWithoutModifiers(fieldType); s << QString::fromLatin1("*").repeated(fieldType->indirections()) << "& cppOut_ptr = "; s << cppField << ';' << endl; @@ -4227,7 +4326,7 @@ void CppGenerator::writeSetterFunction(QTextStream &s, s << ';' << endl << endl; if (isPointerToWrapperType(fieldType)) { - s << INDENT << "Shiboken::Object::keepReference(reinterpret_cast<SbkObject*>(" PYTHON_SELF_VAR "), \""; + s << INDENT << "Shiboken::Object::keepReference(reinterpret_cast<SbkObject*>(self), \""; s << metaField->name() << "\", pyIn);" << endl; } @@ -4240,12 +4339,12 @@ void CppGenerator::writeRichCompareFunction(QTextStream &s, GeneratorContext &co const AbstractMetaClass *metaClass = context.metaClass(); QString baseName = cpythonBaseName(metaClass); s << "static PyObject* "; - s << baseName << "_richcompare(PyObject* " PYTHON_SELF_VAR ", PyObject* " PYTHON_ARG ", int op)" << endl; + s << baseName << "_richcompare(PyObject* self, PyObject* " << PYTHON_ARG << ", int op)" << endl; s << '{' << endl; writeCppSelfDefinition(s, context, false, true); writeUnusedVariableCast(s, QLatin1String(CPP_SELF_VAR)); - s << INDENT << "PyObject* " PYTHON_RETURN_VAR " = 0;" << endl; - s << INDENT << "PythonToCppFunc " PYTHON_TO_CPP_VAR << ';' << endl; + s << INDENT << "PyObject* " << PYTHON_RETURN_VAR << " = 0;" << endl; + s << INDENT << "PythonToCppFunc " << PYTHON_TO_CPP_VAR << ';' << endl; writeUnusedVariableCast(s, QLatin1String(PYTHON_TO_CPP_VAR)); s << endl; @@ -4302,15 +4401,17 @@ void CppGenerator::writeRichCompareFunction(QTextStream &s, GeneratorContext &co CodeSnipList snips = func->injectedCodeSnips(); writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionAny, TypeSystem::TargetLangCode, func, func->arguments().constLast()); } else { - QString expression = QString::fromLatin1("%1%2 %3 (%4" CPP_ARG0 ")") - .arg(func->isPointerOperator() ? QLatin1String("&") : QString(), - QLatin1String(CPP_SELF_VAR), op, - shouldDereferenceAbstractMetaTypePointer(argType) ? QLatin1String("*") : QString()); s << INDENT; if (func->type()) - s << func->type()->cppSignature() << " " CPP_RETURN_VAR " = "; - s << expression << ';' << endl; - s << INDENT << PYTHON_RETURN_VAR " = "; + s << func->type()->cppSignature() << " " << CPP_RETURN_VAR << " = "; + // expression + if (func->isPointerOperator()) + s << '&'; + s << CPP_SELF_VAR << ' ' << op << '('; + if (shouldDereferenceAbstractMetaTypePointer(argType)) + s << '*'; + s << CPP_ARG0 << ");" << endl; + s << INDENT << PYTHON_RETURN_VAR << " = "; if (func->type()) writeToPythonConversion(s, func->type(), metaClass, QLatin1String(CPP_RETURN_VAR)); else @@ -4324,9 +4425,9 @@ void CppGenerator::writeRichCompareFunction(QTextStream &s, GeneratorContext &co s << " else {" << endl; if (operatorId == QLatin1String("Py_EQ") || operatorId == QLatin1String("Py_NE")) { Indentation indent(INDENT); - s << INDENT << PYTHON_RETURN_VAR " = " + s << INDENT << PYTHON_RETURN_VAR << " = " << (operatorId == QLatin1String("Py_EQ") ? "Py_False" : "Py_True") << ';' << endl; - s << INDENT << "Py_INCREF(" PYTHON_RETURN_VAR ");" << endl; + s << INDENT << "Py_INCREF(" << PYTHON_RETURN_VAR << ");" << endl; } else { Indentation indent(INDENT); s << INDENT << "goto " << baseName << "_RichComparison_TypeError;" << endl; @@ -4343,18 +4444,18 @@ void CppGenerator::writeRichCompareFunction(QTextStream &s, GeneratorContext &co } s << INDENT << '}' << endl << endl; - s << INDENT << "if (" PYTHON_RETURN_VAR " && !PyErr_Occurred())" << endl; + s << INDENT << "if (" << PYTHON_RETURN_VAR << " && !PyErr_Occurred())" << endl; { Indentation indent(INDENT); - s << INDENT << "return " PYTHON_RETURN_VAR ";" << endl; + s << INDENT << "return " << PYTHON_RETURN_VAR << ";" << endl; } s << INDENT << baseName << "_RichComparison_TypeError:" << endl; s << INDENT << "PyErr_SetString(PyExc_NotImplementedError, \"operator not implemented.\");" << endl; - s << INDENT << "return " << m_currentErrorCode << ';' << endl << endl; + s << INDENT << returnStatement(m_currentErrorCode) << endl << endl; s << '}' << endl << endl; } -void CppGenerator::writeMethodDefinitionEntry(QTextStream& s, const AbstractMetaFunctionList overloads) +void CppGenerator::writeMethodDefinitionEntry(QTextStream& s, const AbstractMetaFunctionList &overloads) { Q_ASSERT(!overloads.isEmpty()); OverloadData overloadData(overloads, this); @@ -4378,7 +4479,7 @@ void CppGenerator::writeMethodDefinitionEntry(QTextStream& s, const AbstractMeta s << "|METH_STATIC"; } -void CppGenerator::writeMethodDefinition(QTextStream& s, const AbstractMetaFunctionList overloads) +void CppGenerator::writeMethodDefinition(QTextStream& s, const AbstractMetaFunctionList &overloads) { Q_ASSERT(!overloads.isEmpty()); const AbstractMetaFunction* func = overloads.constFirst(); @@ -4510,7 +4611,7 @@ void CppGenerator::writeEnumInitialization(QTextStream& s, const AbstractMetaEnu s << INDENT << "if (!" << cpythonTypeNameExt(cppEnum->typeEntry()) << ')' << endl; { Indentation indent(INDENT); - s << INDENT << "return " << m_currentErrorCode << ';' << endl << endl; + s << INDENT << returnStatement(m_currentErrorCode) << endl << endl; } } @@ -4543,7 +4644,7 @@ void CppGenerator::writeEnumInitialization(QTextStream& s, const AbstractMetaEnu << "))->tp_dict, \"" << enumValue->name() << "\", anonEnumItem) < 0)" << endl; { Indentation indent(INDENT); - s << INDENT << "return " << m_currentErrorCode << ';' << endl; + s << INDENT << returnStatement(m_currentErrorCode) << endl; } s << INDENT << "Py_DECREF(anonEnumItem);" << endl; } @@ -4553,7 +4654,7 @@ void CppGenerator::writeEnumInitialization(QTextStream& s, const AbstractMetaEnu s << enumValueText << ") < 0)" << endl; { Indentation indent(INDENT); - s << INDENT << "return " << m_currentErrorCode << ';' << endl; + s << INDENT << returnStatement(m_currentErrorCode) << endl; } } break; @@ -4564,7 +4665,7 @@ void CppGenerator::writeEnumInitialization(QTextStream& s, const AbstractMetaEnu Indentation indent(INDENT); s << INDENT << enclosingObjectVariable << ", \"" << enumValue->name() << "\", "; s << enumValueText << "))" << endl; - s << INDENT << "return " << m_currentErrorCode << ';' << endl; + s << INDENT << returnStatement(m_currentErrorCode) << endl; } break; case EnumClass: { @@ -4573,7 +4674,7 @@ void CppGenerator::writeEnumInitialization(QTextStream& s, const AbstractMetaEnu Indentation indent(INDENT); s << INDENT << enumVarTypeObj<< ", \"" << enumValue->name() << "\", " << enumValueText << "))" << endl - << INDENT << "return " << m_currentErrorCode << ';' << endl; + << INDENT << returnStatement(m_currentErrorCode) << endl; } break; } @@ -4618,11 +4719,11 @@ void CppGenerator::writeFlagsToLong(QTextStream& s, const AbstractMetaEnum* cppE FlagsTypeEntry* flagsEntry = cppEnum->typeEntry()->flags(); if (!flagsEntry) return; - s << "static PyObject* " << cpythonEnumName(cppEnum) << "_long(PyObject* " PYTHON_SELF_VAR ")" << endl; + s << "static PyObject* " << cpythonEnumName(cppEnum) << "_long(PyObject* self)" << endl; s << "{" << endl; s << INDENT << "int val;" << endl; AbstractMetaType* flagsType = buildAbstractMetaTypeFromTypeEntry(flagsEntry); - s << INDENT << cpythonToCppConversionFunction(flagsType) << PYTHON_SELF_VAR << ", &val);" << endl; + s << INDENT << cpythonToCppConversionFunction(flagsType) << "self, &val);" << endl; s << INDENT << "return Shiboken::Conversions::copyToPython(Shiboken::Conversions::PrimitiveTypeConverter<int>(), &val);" << endl; s << "}" << endl; } @@ -4632,12 +4733,12 @@ void CppGenerator::writeFlagsNonZero(QTextStream& s, const AbstractMetaEnum* cpp FlagsTypeEntry* flagsEntry = cppEnum->typeEntry()->flags(); if (!flagsEntry) return; - s << "static int " << cpythonEnumName(cppEnum) << "__nonzero(PyObject* " PYTHON_SELF_VAR ")" << endl; + s << "static int " << cpythonEnumName(cppEnum) << "__nonzero(PyObject* self)" << endl; s << "{" << endl; s << INDENT << "int val;" << endl; AbstractMetaType* flagsType = buildAbstractMetaTypeFromTypeEntry(flagsEntry); - s << INDENT << cpythonToCppConversionFunction(flagsType) << PYTHON_SELF_VAR << ", &val);" << endl; + s << INDENT << cpythonToCppConversionFunction(flagsType) << "self, &val);" << endl; s << INDENT << "return val != 0;" << endl; s << "}" << endl; } @@ -4679,24 +4780,24 @@ void CppGenerator::writeFlagsNumberMethodsDefinition(QTextStream& s, const Abstr } void CppGenerator::writeFlagsBinaryOperator(QTextStream& s, const AbstractMetaEnum* cppEnum, - QString pyOpName, QString cppOpName) + const QString &pyOpName, const QString &cppOpName) { FlagsTypeEntry* flagsEntry = cppEnum->typeEntry()->flags(); Q_ASSERT(flagsEntry); - s << "PyObject* " << cpythonEnumName(cppEnum) << "___" << pyOpName << "__(PyObject* " PYTHON_SELF_VAR ", PyObject* " PYTHON_ARG ")" << endl; + s << "PyObject* " << cpythonEnumName(cppEnum) << "___" << pyOpName << "__(PyObject* self, PyObject* " << PYTHON_ARG << ")" << endl; s << '{' << endl; AbstractMetaType* flagsType = buildAbstractMetaTypeFromTypeEntry(flagsEntry); - s << INDENT << "::" << flagsEntry->originalName() << " cppResult, " CPP_SELF_VAR ", cppArg;" << endl; + s << INDENT << "::" << flagsEntry->originalName() << " cppResult, " << CPP_SELF_VAR << ", cppArg;" << endl; s << "#ifdef IS_PY3K" << endl; - s << INDENT << CPP_SELF_VAR " = (::" << flagsEntry->originalName() << ")(int)PyLong_AsLong(" PYTHON_SELF_VAR ");" << endl; - s << INDENT << "cppArg = (" << flagsEntry->originalName() << ")(int)PyLong_AsLong(" PYTHON_ARG ");" << endl; + s << INDENT << CPP_SELF_VAR << " = (::" << flagsEntry->originalName() << ")(int)PyLong_AsLong(self);" << endl; + s << INDENT << "cppArg = (" << flagsEntry->originalName() << ")(int)PyLong_AsLong(" << PYTHON_ARG << ");" << endl; s << "#else" << endl; - s << INDENT << CPP_SELF_VAR " = (::" << flagsEntry->originalName() << ")(int)PyInt_AsLong(" PYTHON_SELF_VAR ");" << endl; - s << INDENT << "cppArg = (" << flagsEntry->originalName() << ")(int)PyInt_AsLong(" PYTHON_ARG ");" << endl; + s << INDENT << CPP_SELF_VAR << " = (::" << flagsEntry->originalName() << ")(int)PyInt_AsLong(self);" << endl; + s << INDENT << "cppArg = (" << flagsEntry->originalName() << ")(int)PyInt_AsLong(" << PYTHON_ARG << ");" << endl; s << "#endif" << endl << endl; - s << INDENT << "cppResult = " CPP_SELF_VAR " " << cppOpName << " cppArg;" << endl; + s << INDENT << "cppResult = " << CPP_SELF_VAR << " " << cppOpName << " cppArg;" << endl; s << INDENT << "return "; writeToPythonConversion(s, flagsType, 0, QLatin1String("cppResult")); s << ';' << endl; @@ -4704,23 +4805,24 @@ void CppGenerator::writeFlagsBinaryOperator(QTextStream& s, const AbstractMetaEn } void CppGenerator::writeFlagsUnaryOperator(QTextStream& s, const AbstractMetaEnum* cppEnum, - QString pyOpName, QString cppOpName, bool boolResult) + const QString &pyOpName, + const QString &cppOpName, bool boolResult) { FlagsTypeEntry* flagsEntry = cppEnum->typeEntry()->flags(); Q_ASSERT(flagsEntry); - s << "PyObject* " << cpythonEnumName(cppEnum) << "___" << pyOpName << "__(PyObject* " PYTHON_SELF_VAR ", PyObject* " PYTHON_ARG ")" << endl; + s << "PyObject* " << cpythonEnumName(cppEnum) << "___" << pyOpName << "__(PyObject* self, PyObject* " << PYTHON_ARG << ")" << endl; s << '{' << endl; AbstractMetaType* flagsType = buildAbstractMetaTypeFromTypeEntry(flagsEntry); - s << INDENT << "::" << flagsEntry->originalName() << " " CPP_SELF_VAR ";" << endl; - s << INDENT << cpythonToCppConversionFunction(flagsType) << PYTHON_SELF_VAR << ", &" CPP_SELF_VAR ");" << endl; + s << INDENT << "::" << flagsEntry->originalName() << " " << CPP_SELF_VAR << ";" << endl; + s << INDENT << cpythonToCppConversionFunction(flagsType) << "self, &" << CPP_SELF_VAR << ");" << endl; s << INDENT; if (boolResult) s << "bool"; else s << "::" << flagsEntry->originalName(); - s << " cppResult = " << cppOpName << CPP_SELF_VAR ";" << endl; + s << " cppResult = " << cppOpName << CPP_SELF_VAR << ';' << endl; s << INDENT << "return "; if (boolResult) s << "PyBool_FromLong(cppResult)"; @@ -4846,8 +4948,16 @@ void CppGenerator::writeClassRegister(QTextStream &s, else s << INDENT << "0," << endl; - // 9:isInnerClass - s << INDENT << (hasEnclosingClass ? "true" : "false") << endl; + // 9:wrapperflags + QByteArrayList wrapperFlags; + if (hasEnclosingClass) + wrapperFlags.append(QByteArrayLiteral("Shiboken::ObjectType::WrapperFlags::InnerClass")); + if (metaClass->deleteInMainThread()) + wrapperFlags.append(QByteArrayLiteral("Shiboken::ObjectType::WrapperFlags::DeleteInMainThread")); + if (wrapperFlags.isEmpty()) + s << INDENT << '0'; + else + s << INDENT << wrapperFlags.join(" | "); } s << INDENT << ");" << endl; s << INDENT << endl; @@ -5042,25 +5152,27 @@ void CppGenerator::writeTypeDiscoveryFunction(QTextStream& s, const AbstractMeta s << "}\n\n"; } -QString CppGenerator::writeSmartPointerGetterCast() { - return QLatin1String("const_cast<char *>(" SMART_POINTER_GETTER ")"); +QString CppGenerator::writeSmartPointerGetterCast() +{ + return QLatin1String("const_cast<char *>(") + + QLatin1String(SMART_POINTER_GETTER) + QLatin1Char(')'); } void CppGenerator::writeSetattroFunction(QTextStream &s, GeneratorContext &context) { const AbstractMetaClass* metaClass = context.metaClass(); - s << "static int " << cpythonSetattroFunctionName(metaClass) << "(PyObject* " PYTHON_SELF_VAR ", PyObject* name, PyObject* value)" << endl; + s << "static int " << cpythonSetattroFunctionName(metaClass) << "(PyObject* self, PyObject* name, PyObject* value)" << endl; s << '{' << endl; if (usePySideExtensions()) { - s << INDENT << "Shiboken::AutoDecRef pp(reinterpret_cast<PyObject*>(PySide::Property::getObject(" PYTHON_SELF_VAR ", name)));" << endl; + s << INDENT << "Shiboken::AutoDecRef pp(reinterpret_cast<PyObject*>(PySide::Property::getObject(self, name)));" << endl; s << INDENT << "if (!pp.isNull())" << endl; Indentation indent(INDENT); - s << INDENT << "return PySide::Property::setValue(reinterpret_cast<PySideProperty*>(pp.object()), " PYTHON_SELF_VAR ", value);" << endl; + s << INDENT << "return PySide::Property::setValue(reinterpret_cast<PySideProperty*>(pp.object()), self, value);" << endl; } if (context.forSmartPointer()) { s << INDENT << "// Try to find the 'name' attribute, by retrieving the PyObject for the corresponding C++ object held by the smart pointer." << endl; - s << INDENT << "PyObject *rawObj = PyObject_CallMethod(" PYTHON_SELF_VAR ", " + s << INDENT << "PyObject *rawObj = PyObject_CallMethod(self, " << writeSmartPointerGetterCast() << ", 0);" << endl; s << INDENT << "if (rawObj) {" << endl; { @@ -5078,7 +5190,7 @@ void CppGenerator::writeSetattroFunction(QTextStream &s, GeneratorContext &conte } - s << INDENT << "return PyObject_GenericSetAttr(" PYTHON_SELF_VAR ", name, value);" << endl; + s << INDENT << "return PyObject_GenericSetAttr(self, name, value);" << endl; s << '}' << endl; } @@ -5088,27 +5200,29 @@ static inline QString qMetaObjectClassName() { return QStringLiteral("QMetaObjec void CppGenerator::writeGetattroFunction(QTextStream& s, GeneratorContext &context) { const AbstractMetaClass* metaClass = context.metaClass(); - s << "static PyObject* " << cpythonGetattroFunctionName(metaClass) << "(PyObject* " PYTHON_SELF_VAR ", PyObject* name)" << endl; + s << "static PyObject* " << cpythonGetattroFunctionName(metaClass) << "(PyObject* self, PyObject* name)" << endl; s << '{' << endl; QString getattrFunc; if (usePySideExtensions() && metaClass->isQObject()) { AbstractMetaClass *qobjectClass = AbstractMetaClass::findClass(classes(), qObjectClassName()); - getattrFunc = QString::fromLatin1("PySide::getMetaDataFromQObject(%1, " PYTHON_SELF_VAR ", name)") - .arg(cpythonWrapperCPtr(qobjectClass, QLatin1String(PYTHON_SELF_VAR))); + QTextStream(&getattrFunc) << "PySide::getMetaDataFromQObject(" + << cpythonWrapperCPtr(qobjectClass, QLatin1String("self")) + << ", self, name)"; } else { - getattrFunc = QLatin1String("PyObject_GenericGetAttr(" PYTHON_SELF_VAR ", name)"); + getattrFunc = QLatin1String("PyObject_GenericGetAttr(") + QLatin1String("self") + + QLatin1String(", name)"); } if (classNeedsGetattroFunction(metaClass)) { - s << INDENT << "if (" PYTHON_SELF_VAR ") {" << endl; + s << INDENT << "if (self) {" << endl; { Indentation indent(INDENT); s << INDENT << "// Search the method in the instance dict" << endl; - s << INDENT << "if (reinterpret_cast<SbkObject*>(" PYTHON_SELF_VAR ")->ob_dict) {" << endl; + s << INDENT << "if (reinterpret_cast<SbkObject*>(self)->ob_dict) {" << endl; { Indentation indent(INDENT); - s << INDENT << "PyObject* meth = PyDict_GetItem(reinterpret_cast<SbkObject*>(" PYTHON_SELF_VAR ")->ob_dict, name);" << endl; + s << INDENT << "PyObject* meth = PyDict_GetItem(reinterpret_cast<SbkObject*>(self)->ob_dict, name);" << endl; s << INDENT << "if (meth) {" << endl; { Indentation indent(INDENT); @@ -5119,16 +5233,16 @@ void CppGenerator::writeGetattroFunction(QTextStream& s, GeneratorContext &conte } s << INDENT << '}' << endl; s << INDENT << "// Search the method in the type dict" << endl; - s << INDENT << "if (Shiboken::Object::isUserType(" PYTHON_SELF_VAR ")) {" << endl; + s << INDENT << "if (Shiboken::Object::isUserType(self)) {" << endl; { Indentation indent(INDENT); // PYSIDE-772: Perform optimized name mangling. - s << INDENT << "Shiboken::AutoDecRef tmp(_Pep_PrivateMangle(" PYTHON_SELF_VAR ", name));" << endl; - s << INDENT << "PyObject *meth = PyDict_GetItem(Py_TYPE(" PYTHON_SELF_VAR ")->tp_dict, tmp);" << endl; + s << INDENT << "Shiboken::AutoDecRef tmp(_Pep_PrivateMangle(self, name));" << endl; + s << INDENT << "PyObject *meth = PyDict_GetItem(Py_TYPE(self)->tp_dict, tmp);" << endl; s << INDENT << "if (meth)" << endl; { Indentation indent(INDENT); - s << INDENT << "return PyFunction_Check(meth) ? SBK_PyMethod_New(meth, " PYTHON_SELF_VAR ") : " << getattrFunc << ';' << endl; + s << INDENT << "return PyFunction_Check(meth) ? SBK_PyMethod_New(meth, self) : " << getattrFunc << ';' << endl; } } s << INDENT << '}' << endl; @@ -5147,7 +5261,7 @@ void CppGenerator::writeGetattroFunction(QTextStream& s, GeneratorContext &conte s << INDENT << "};" << endl; s << INDENT << "if (Shiboken::String::compare(name, \"" << func->name() << "\") == 0)" << endl; Indentation indent(INDENT); - s << INDENT << "return PyCFunction_NewEx(&non_static_" << defName << ", " PYTHON_SELF_VAR ", 0);" << endl; + s << INDENT << "return PyCFunction_NewEx(&non_static_" << defName << ", self, 0);" << endl; } } s << INDENT << '}' << endl; @@ -5168,7 +5282,7 @@ void CppGenerator::writeGetattroFunction(QTextStream& s, GeneratorContext &conte s << INDENT << "// Try to find the 'name' attribute, by retrieving the PyObject for " "the corresponding C++ object held by the smart pointer." << endl; - s << INDENT << "PyObject *rawObj = PyObject_CallMethod(" PYTHON_SELF_VAR ", " + s << INDENT << "PyObject *rawObj = PyObject_CallMethod(self, " << writeSmartPointerGetterCast() << ", 0);" << endl; s << INDENT << "if (rawObj) {" << endl; { @@ -5289,15 +5403,11 @@ bool CppGenerator::finishGeneration() QString moduleFileName(outputDirectory() + QLatin1Char('/') + subDirectoryForPackage(packageName())); moduleFileName += QLatin1Char('/') + moduleName().toLower() + QLatin1String("_module_wrapper.cpp"); - QFile file(moduleFileName); - verifyDirectoryFor(file); - if (!file.open(QFile::WriteOnly)) { - qCWarning(lcShiboken).noquote().nospace() - << "Error writing file: " << QDir::toNativeSeparators(moduleFileName); - return false; - } - QTextStream s(&file); + verifyDirectoryFor(moduleFileName); + FileOut file(moduleFileName); + + QTextStream &s = file.stream; // write license comment s << licenseComment() << endl; @@ -5328,13 +5438,12 @@ bool CppGenerator::finishGeneration() } TypeDatabase* typeDb = TypeDatabase::instance(); - TypeSystemTypeEntry* moduleEntry = reinterpret_cast<TypeSystemTypeEntry*>(typeDb->findType(packageName())); + const TypeSystemTypeEntry *moduleEntry = typeDb->findTypeSystemType(packageName()); + Q_ASSERT(moduleEntry); //Extra includes s << endl << "// Extra includes" << endl; - QVector<Include> extraIncludes; - if (moduleEntry) - extraIncludes = moduleEntry->extraIncludes(); + QVector<Include> extraIncludes = moduleEntry->extraIncludes(); for (AbstractMetaEnum *cppEnum : qAsConst(globalEnums)) extraIncludes.append(cppEnum->typeEntry()->extraIncludes()); qSort(extraIncludes.begin(), extraIncludes.end()); @@ -5343,14 +5452,15 @@ bool CppGenerator::finishGeneration() s << endl; s << "// Current module's type array." << endl; - s << "PyTypeObject** " << cppApiVariableName() << ';' << endl; + s << "PyTypeObject** " << cppApiVariableName() << " = nullptr;" << endl; + + s << "// Current module's PyObject pointer." << endl; + s << "PyObject* " << pythonModuleObjectName() << " = nullptr;" << endl; s << "// Current module's converter array." << endl; - s << "SbkConverter** " << convertersVariableName() << ';' << endl; + s << "SbkConverter** " << convertersVariableName() << " = nullptr;" << endl; - CodeSnipList snips; - if (moduleEntry) - snips = moduleEntry->codeSnips(); + const CodeSnipList snips = moduleEntry->codeSnips(); // module inject-code native/beginning if (!snips.isEmpty()) { @@ -5519,6 +5629,9 @@ bool CppGenerator::finishGeneration() s << moduleName() << "_methods);" << endl; s << "#endif" << endl << endl; + s << INDENT << "// Make module available from global scope" << endl; + s << INDENT << pythonModuleObjectName() << " = module;" << endl << endl; + //s << INDENT << "// Initialize converters for primitive types." << endl; //s << INDENT << "initConverters();" << endl << endl; @@ -5619,7 +5732,7 @@ bool CppGenerator::finishGeneration() s << "SBK_MODULE_INIT_FUNCTION_END" << endl; - return true; + return file.done() != FileOut::Failure; } static ArgumentOwner getArgumentOwner(const AbstractMetaFunction* func, int argIndex) @@ -5663,22 +5776,20 @@ bool CppGenerator::writeParentChildManagement(QTextStream& s, const AbstractMeta if (parentIndex == 0) { parentVariable = QLatin1String(PYTHON_RETURN_VAR); } else if (parentIndex == -1) { - parentVariable = QLatin1String(PYTHON_SELF_VAR); + parentVariable = QLatin1String("self"); } else { parentVariable = usePyArgs - ? QString::fromLatin1(PYTHON_ARGS "[%1]").arg(parentIndex - 1) - : QLatin1String(PYTHON_ARG); + ? pythonArgsAt(parentIndex - 1) : QLatin1String(PYTHON_ARG); } } if (childIndex == 0) { childVariable = QLatin1String(PYTHON_RETURN_VAR); } else if (childIndex == -1) { - childVariable = QLatin1String(PYTHON_SELF_VAR); + childVariable = QLatin1String("self"); } else { childVariable = usePyArgs - ? QString::fromLatin1(PYTHON_ARGS "[%1]").arg(childIndex - 1) - : QLatin1String(PYTHON_ARG); + ? pythonArgsAt(childIndex - 1) : QLatin1String(PYTHON_ARG); } s << INDENT << "Shiboken::Object::setParent(" << parentVariable << ", " << childVariable << ");\n"; @@ -5717,7 +5828,7 @@ void CppGenerator::writeReturnValueHeuristics(QTextStream& s, const AbstractMeta ArgumentOwner argOwner = getArgumentOwner(func, ArgumentOwner::ReturnIndex); if (argOwner.action == ArgumentOwner::Invalid || argOwner.index != ArgumentOwner::ThisIndex) { if (isPointerToWrapperType(type)) - s << INDENT << "Shiboken::Object::setParent(" << self << ", " PYTHON_RETURN_VAR ");" << endl; + s << INDENT << "Shiboken::Object::setParent(self, " << PYTHON_RETURN_VAR << ");" << endl; } } @@ -5737,19 +5848,19 @@ void CppGenerator::writeStdListWrapperMethods(QTextStream &s, GeneratorContext & ErrorCode errorCode(0); // __len__ - s << "Py_ssize_t " << cpythonBaseName(metaClass->typeEntry()) << "__len__(PyObject* " PYTHON_SELF_VAR ")" << endl; + s << "Py_ssize_t " << cpythonBaseName(metaClass->typeEntry()) << "__len__(PyObject* self)" << endl; s << '{' << endl; writeCppSelfDefinition(s, context); - s << INDENT << "return " CPP_SELF_VAR "->size();" << endl; + s << INDENT << "return " << CPP_SELF_VAR << "->size();" << endl; s << '}' << endl; // __getitem__ - s << "PyObject* " << cpythonBaseName(metaClass->typeEntry()) << "__getitem__(PyObject* " PYTHON_SELF_VAR ", Py_ssize_t _i)" << endl; + s << "PyObject* " << cpythonBaseName(metaClass->typeEntry()) << "__getitem__(PyObject* self, Py_ssize_t _i)" << endl; s << '{' << endl; writeCppSelfDefinition(s, context); writeIndexError(s, QLatin1String("index out of bounds")); - s << INDENT << metaClass->qualifiedCppName() << "::iterator _item = " CPP_SELF_VAR "->begin();" << endl; + s << INDENT << metaClass->qualifiedCppName() << "::iterator _item = " << CPP_SELF_VAR << "->begin();" << endl; s << INDENT << "for (Py_ssize_t pos = 0; pos < _i; pos++) _item++;" << endl; const AbstractMetaType* itemType = metaClass->templateBaseClassInstantiations().constFirst(); @@ -5761,7 +5872,7 @@ void CppGenerator::writeStdListWrapperMethods(QTextStream &s, GeneratorContext & // __setitem__ ErrorCode errorCode2(-1); - s << "int " << cpythonBaseName(metaClass->typeEntry()) << "__setitem__(PyObject* " PYTHON_SELF_VAR ", Py_ssize_t _i, PyObject* pyArg)" << endl; + s << "int " << cpythonBaseName(metaClass->typeEntry()) << "__setitem__(PyObject* self, Py_ssize_t _i, PyObject* pyArg)" << endl; s << '{' << endl; writeCppSelfDefinition(s, context); writeIndexError(s, QLatin1String("list assignment index out of range")); @@ -5779,7 +5890,7 @@ void CppGenerator::writeStdListWrapperMethods(QTextStream &s, GeneratorContext & s << INDENT << '}' << endl; writeArgumentConversion(s, itemType, QLatin1String("cppValue"), QLatin1String("pyArg"), metaClass); - s << INDENT << metaClass->qualifiedCppName() << "::iterator _item = " CPP_SELF_VAR "->begin();" << endl; + s << INDENT << metaClass->qualifiedCppName() << "::iterator _item = " << CPP_SELF_VAR << "->begin();" << endl; s << INDENT << "for (Py_ssize_t pos = 0; pos < _i; pos++) _item++;" << endl; s << INDENT << "*_item = cppValue;" << endl; s << INDENT << "return 0;" << endl; @@ -5787,11 +5898,11 @@ void CppGenerator::writeStdListWrapperMethods(QTextStream &s, GeneratorContext & } void CppGenerator::writeIndexError(QTextStream& s, const QString& errorMsg) { - s << INDENT << "if (_i < 0 || _i >= (Py_ssize_t) " CPP_SELF_VAR "->size()) {" << endl; + s << INDENT << "if (_i < 0 || _i >= (Py_ssize_t) " << CPP_SELF_VAR << "->size()) {" << endl; { Indentation indent(INDENT); s << INDENT << "PyErr_SetString(PyExc_IndexError, \"" << errorMsg << "\");" << endl; - s << INDENT << "return " << m_currentErrorCode << ';' << endl; + s << INDENT << returnStatement(m_currentErrorCode) << endl; } s << INDENT << '}' << endl; } @@ -5808,7 +5919,10 @@ QString CppGenerator::writeReprFunction(QTextStream &s, GeneratorContext &contex s << INDENT << "QBuffer buffer;" << endl; s << INDENT << "buffer.open(QBuffer::ReadWrite);" << endl; s << INDENT << "QDebug dbg(&buffer);" << endl; - s << INDENT << "dbg << " << (metaClass->typeEntry()->isValue() ? "*" : "") << CPP_SELF_VAR ";" << endl; + s << INDENT << "dbg << "; + if (metaClass->typeEntry()->isValue()) + s << '*'; + s << CPP_SELF_VAR << ';' << endl; s << INDENT << "buffer.close();" << endl; s << INDENT << "QByteArray str = buffer.data();" << endl; s << INDENT << "int idx = str.indexOf('(');" << endl; diff --git a/sources/shiboken2/generator/shiboken2/cppgenerator.h b/sources/shiboken2/generator/shiboken2/cppgenerator.h index 1b59bcda7..55a1c265d 100644 --- a/sources/shiboken2/generator/shiboken2/cppgenerator.h +++ b/sources/shiboken2/generator/shiboken2/cppgenerator.h @@ -234,8 +234,8 @@ private: void writeClassDefinition(QTextStream &s, const AbstractMetaClass *metaClass, GeneratorContext &classContext); - void writeMethodDefinitionEntry(QTextStream& s, const AbstractMetaFunctionList overloads); - void writeMethodDefinition(QTextStream& s, const AbstractMetaFunctionList overloads); + void writeMethodDefinitionEntry(QTextStream& s, const AbstractMetaFunctionList &overloads); + void writeMethodDefinition(QTextStream& s, const AbstractMetaFunctionList &overloads); void writeSignatureInfo(QTextStream &s, const AbstractMetaFunctionList &overloads); /// Writes the implementation of all methods part of python sequence protocol void writeSequenceMethods(QTextStream &s, @@ -275,9 +275,10 @@ private: void writeFlagsNonZero(QTextStream& s, const AbstractMetaEnum* cppEnum); void writeFlagsNumberMethodsDefinition(QTextStream& s, const AbstractMetaEnum* cppEnum); void writeFlagsBinaryOperator(QTextStream& s, const AbstractMetaEnum* cppEnum, - QString pyOpName, QString cppOpName); + const QString &pyOpName, const QString &cppOpName); void writeFlagsUnaryOperator(QTextStream& s, const AbstractMetaEnum* cppEnum, - QString pyOpName, QString cppOpName, bool boolResult = false); + const QString &pyOpName, const QString &cppOpName, + bool boolResult = false); /// Writes the function that registers the multiple inheritance information for the classes that need it. void writeMultipleInheritanceInitializerFunction(QTextStream& s, const AbstractMetaClass* metaClass); @@ -292,7 +293,7 @@ private: void writeParentChildManagement(QTextStream& s, const AbstractMetaFunction* func, bool userHeuristicForReturn); bool writeParentChildManagement(QTextStream& s, const AbstractMetaFunction* func, int argIndex, bool userHeuristicPolicy); - void writeReturnValueHeuristics(QTextStream& s, const AbstractMetaFunction* func, const QString& self = QLatin1String(PYTHON_SELF_VAR)); + void writeReturnValueHeuristics(QTextStream& s, const AbstractMetaFunction* func, const QString& self = QLatin1String("self")); void writeInitQtMetaTypeFunctionBody(QTextStream &s, GeneratorContext &context) const; /** @@ -327,7 +328,9 @@ private: QString writeReprFunction(QTextStream &s, GeneratorContext &context); - bool hasBoolCast(const AbstractMetaClass* metaClass) const; + const AbstractMetaFunction *boolCast(const AbstractMetaClass* metaClass) const; + bool hasBoolCast(const AbstractMetaClass* metaClass) const + { return boolCast(metaClass) != nullptr; } // Number protocol structure members names. static QHash<QString, QString> m_nbFuncs; diff --git a/sources/shiboken2/generator/shiboken2/headergenerator.cpp b/sources/shiboken2/generator/shiboken2/headergenerator.cpp index 8fde3cd31..8881d71f4 100644 --- a/sources/shiboken2/generator/shiboken2/headergenerator.cpp +++ b/sources/shiboken2/generator/shiboken2/headergenerator.cpp @@ -32,6 +32,8 @@ #include <reporthandler.h> #include <fileout.h> +#include <algorithm> + #include <QtCore/QDir> #include <QtCore/QTextStream> #include <QtCore/QVariant> @@ -49,11 +51,10 @@ QString HeaderGenerator::fileNameForContext(GeneratorContext &context) const QString fileNameBase = metaClass->qualifiedCppName().toLower(); fileNameBase.replace(QLatin1String("::"), QLatin1String("_")); return fileNameBase + fileNameSuffix(); - } else { - const AbstractMetaType *smartPointerType = context.preciseType(); - QString fileNameBase = getFileNameBaseForSmartPointer(smartPointerType, metaClass); - return fileNameBase + fileNameSuffix(); } + const AbstractMetaType *smartPointerType = context.preciseType(); + QString fileNameBase = getFileNameBaseForSmartPointer(smartPointerType, metaClass); + return fileNameBase + fileNameSuffix(); } void HeaderGenerator::writeCopyCtor(QTextStream& s, const AbstractMetaClass* metaClass) const @@ -116,8 +117,6 @@ void HeaderGenerator::generateClass(QTextStream &s, GeneratorContext &classConte if (!avoidProtectedHack()) s << "#define protected public" << endl << endl; - s << "#include <shiboken.h>" << endl << endl; - //Includes s << metaClass->typeEntry()->include() << endl; @@ -173,7 +172,7 @@ void HeaderGenerator::generateClass(QTextStream &s, GeneratorContext &classConte s << INDENT << "void* qt_metacast(const char* _clname) override;" << endl; } - if (m_inheritedOverloads.size()) { + if (!m_inheritedOverloads.isEmpty()) { s << INDENT << "// Inherited overloads, because the using keyword sux" << endl; writeInheritedOverloads(s); m_inheritedOverloads.clear(); @@ -230,7 +229,7 @@ void HeaderGenerator::writeFunction(QTextStream& s, const AbstractMetaFunction* QString argName = arg->name(); const TypeEntry* enumTypeEntry = 0; if (arg->type()->isFlags()) - enumTypeEntry = reinterpret_cast<const FlagsTypeEntry*>(arg->type()->typeEntry())->originator(); + enumTypeEntry = static_cast<const FlagsTypeEntry*>(arg->type()->typeEntry())->originator(); else if (arg->type()->isEnum()) enumTypeEntry = arg->type()->typeEntry(); if (enumTypeEntry) @@ -284,49 +283,86 @@ void HeaderGenerator::writeFunction(QTextStream& s, const AbstractMetaFunction* } } -static void _writeTypeIndexDefineLine(QTextStream& s, const QString& variableName, int typeIndex) +static void _writeTypeIndexValue(QTextStream& s, const QString& variableName, + int typeIndex) { - s << "#define "; - s.setFieldWidth(60); + s << " "; + s.setFieldWidth(56); s << variableName; s.setFieldWidth(0); - s << ' ' << typeIndex << endl; + s << " = " << typeIndex; +} + +static inline void _writeTypeIndexValueLine(QTextStream& s, + const QString& variableName, + int typeIndex) +{ + _writeTypeIndexValue(s, variableName, typeIndex); + s << ",\n"; } -void HeaderGenerator::writeTypeIndexDefineLine(QTextStream& s, const TypeEntry* typeEntry) + +void HeaderGenerator::writeTypeIndexValueLine(QTextStream& s, const TypeEntry* typeEntry) { if (!typeEntry || !typeEntry->generateCode()) return; s.setFieldAlignment(QTextStream::AlignLeft); - int typeIndex = getTypeIndex(typeEntry); - _writeTypeIndexDefineLine(s, getTypeIndexVariableName(typeEntry), typeIndex); + const int typeIndex = typeEntry->sbkIndex(); + _writeTypeIndexValueLine(s, getTypeIndexVariableName(typeEntry), typeIndex); if (typeEntry->isComplex()) { - const ComplexTypeEntry* cType = reinterpret_cast<const ComplexTypeEntry*>(typeEntry); + const ComplexTypeEntry* cType = static_cast<const ComplexTypeEntry*>(typeEntry); if (cType->baseContainerType()) { const AbstractMetaClass *metaClass = AbstractMetaClass::findClass(classes(), cType); if (metaClass->templateBaseClass()) - _writeTypeIndexDefineLine(s, getTypeIndexVariableName(metaClass, true), typeIndex); + _writeTypeIndexValueLine(s, getTypeIndexVariableName(metaClass, true), typeIndex); } } if (typeEntry->isEnum()) { - const EnumTypeEntry* ete = reinterpret_cast<const EnumTypeEntry*>(typeEntry); + const EnumTypeEntry* ete = static_cast<const EnumTypeEntry*>(typeEntry); if (ete->flags()) - writeTypeIndexDefineLine(s, ete->flags()); + writeTypeIndexValueLine(s, ete->flags()); } } -void HeaderGenerator::writeTypeIndexDefine(QTextStream& s, const AbstractMetaClass* metaClass) +void HeaderGenerator::writeTypeIndexValueLines(QTextStream& s, const AbstractMetaClass* metaClass) { if (!metaClass->typeEntry()->generateCode()) return; - writeTypeIndexDefineLine(s, metaClass->typeEntry()); + writeTypeIndexValueLine(s, metaClass->typeEntry()); const AbstractMetaEnumList &enums = metaClass->enums(); for (const AbstractMetaEnum *metaEnum : enums) { if (metaEnum->isPrivate()) continue; - writeTypeIndexDefineLine(s, metaEnum->typeEntry()); + writeTypeIndexValueLine(s, metaEnum->typeEntry()); } } +// Format the typedefs for the typedef entries to be generated +static void formatTypeDefEntries(QTextStream &s) +{ + QVector<const TypedefEntry *> entries; + const auto typeDbEntries = TypeDatabase::instance()->typedefEntries(); + for (auto it = typeDbEntries.cbegin(), end = typeDbEntries.cend(); it != end; ++it) { + if (it.value()->generateCode() != 0) + entries.append(it.value()); + } + if (entries.isEmpty()) + return; + s << "\n// typedef entries\n"; + for (const auto e : entries) { + const QString name = e->qualifiedCppName(); + // Fixme: simplify by using nested namespaces in C++ 17. + const auto components = name.splitRef(QLatin1String("::")); + const int nameSpaceCount = components.size() - 1; + for (int n = 0; n < nameSpaceCount; ++n) + s << "namespace " << components.at(n) << " {\n"; + s << "using " << components.constLast() << " = " << e->sourceType() << ";\n"; + for (int n = 0; n < nameSpaceCount; ++n) + s << "}\n"; + } + s << '\n'; +} + + bool HeaderGenerator::finishGeneration() { // Generate the main header for this module. @@ -342,47 +378,49 @@ bool HeaderGenerator::finishGeneration() Indentation indent(INDENT); - macrosStream << "// Type indices" << endl; + macrosStream << "// Type indices\nenum : int {\n"; AbstractMetaEnumList globalEnums = this->globalEnums(); - const AbstractMetaClassList &classList = classes(); + AbstractMetaClassList classList = classes(); + + std::sort(classList.begin(), classList.end(), [](AbstractMetaClass *a, AbstractMetaClass* b) { + return a->typeEntry()->sbkIndex() < b->typeEntry()->sbkIndex(); + }); + for (const AbstractMetaClass *metaClass : classList) { - writeTypeIndexDefine(macrosStream, metaClass); + writeTypeIndexValueLines(macrosStream, metaClass); lookForEnumsInClassesNotToBeGenerated(globalEnums, metaClass); } for (const AbstractMetaEnum *metaEnum : qAsConst(globalEnums)) - writeTypeIndexDefineLine(macrosStream, metaEnum->typeEntry()); + writeTypeIndexValueLine(macrosStream, metaEnum->typeEntry()); // Write the smart pointer define indexes. int smartPointerCountIndex = getMaxTypeIndex(); int smartPointerCount = 0; const QVector<const AbstractMetaType *> &instantiatedSmartPtrs = instantiatedSmartPointers(); for (const AbstractMetaType *metaType : instantiatedSmartPtrs) { - QString variableName = getTypeIndexVariableName(metaType); - macrosStream << "#define "; - macrosStream.setFieldWidth(60); - macrosStream << variableName; - macrosStream.setFieldWidth(0); - macrosStream << ' ' << smartPointerCountIndex << " // " << metaType->cppSignature() - << endl; + _writeTypeIndexValue(macrosStream, getTypeIndexVariableName(metaType), + smartPointerCountIndex); + macrosStream << ", // " << metaType->cppSignature() << endl; ++smartPointerCountIndex; ++smartPointerCount; } + _writeTypeIndexValue(macrosStream, + QLatin1String("SBK_") + moduleName() + QLatin1String("_IDX_COUNT"), + getMaxTypeIndex() + smartPointerCount); + macrosStream << "\n};\n"; - macrosStream << "#define "; - macrosStream.setFieldWidth(60); - macrosStream << QLatin1String("SBK_") + moduleName() + QLatin1String("_IDX_COUNT"); - macrosStream.setFieldWidth(0); - macrosStream << ' ' << getMaxTypeIndex() + smartPointerCount << endl << endl; macrosStream << "// This variable stores all Python types exported by this module." << endl; macrosStream << "extern PyTypeObject** " << cppApiVariableName() << ';' << endl << endl; + macrosStream << "// This variable stores the Python module object exported by this module." << endl; + macrosStream << "extern PyObject* " << pythonModuleObjectName() << ';' << endl << endl; macrosStream << "// This variable stores all type converters exported by this module." << endl; macrosStream << "extern SbkConverter** " << convertersVariableName() << ';' << endl << endl; // TODO-CONVERTER ------------------------------------------------------------------------------ // Using a counter would not do, a fix must be made to APIExtractor's getTypeIndex(). - macrosStream << "// Converter indices" << endl; + macrosStream << "// Converter indices\nenum : int {\n"; const PrimitiveTypeEntryList &primitives = primitiveTypes(); int pCount = 0; for (const PrimitiveTypeEntry *ptype : primitives) { @@ -393,28 +431,25 @@ bool HeaderGenerator::finishGeneration() if (!ptype->generateCode() || !ptype->customConversion()) continue; - _writeTypeIndexDefineLine(macrosStream, getTypeIndexVariableName(ptype), pCount++); + _writeTypeIndexValueLine(macrosStream, getTypeIndexVariableName(ptype), pCount++); } const QVector<const AbstractMetaType *> &containers = instantiatedContainers(); for (const AbstractMetaType *container : containers) { - //_writeTypeIndexDefineLine(macrosStream, getTypeIndexVariableName(container), pCount); - // DEBUG - QString variableName = getTypeIndexVariableName(container); - macrosStream << "#define "; - macrosStream.setFieldWidth(60); - macrosStream << variableName; - macrosStream.setFieldWidth(0); - macrosStream << ' ' << pCount << " // " << container->cppSignature() << endl; - // DEBUG + _writeTypeIndexValue(macrosStream, getTypeIndexVariableName(container), pCount); + macrosStream << ", // " << container->cppSignature() << endl; pCount++; } // Because on win32 the compiler will not accept a zero length array. if (pCount == 0) pCount++; - _writeTypeIndexDefineLine(macrosStream, QStringLiteral("SBK_%1_CONVERTERS_IDX_COUNT").arg(moduleName()), pCount); - macrosStream << endl; + _writeTypeIndexValue(macrosStream, QStringLiteral("SBK_%1_CONVERTERS_IDX_COUNT") + .arg(moduleName()), pCount); + macrosStream << "\n};\n"; + + formatTypeDefEntries(macrosStream); + // TODO-CONVERTER ------------------------------------------------------------------------------ macrosStream << "// Macros for type check" << endl; @@ -474,12 +509,6 @@ bool HeaderGenerator::finishGeneration() s << "#include <sbkpython.h>" << endl; s << "#include <sbkconverter.h>" << endl; - s << "#include <sbkenum.h>" << endl; - s << "#include <basewrapper.h>" << endl; - s << "#include <bindingmanager.h>" << endl; - s << "#include <memory>" << endl << endl; - if (usePySideExtensions()) - s << "#include <pysidesignal.h>" << endl; QStringList requiredTargetImports = TypeDatabase::instance()->requiredTargetImports(); if (!requiredTargetImports.isEmpty()) { @@ -580,7 +609,7 @@ void HeaderGenerator::writeInheritedOverloads(QTextStream& s) QString argName = arg->name(); const TypeEntry* enumTypeEntry = 0; if (arg->type()->isFlags()) - enumTypeEntry = reinterpret_cast<const FlagsTypeEntry*>(arg->type()->typeEntry())->originator(); + enumTypeEntry = static_cast<const FlagsTypeEntry*>(arg->type()->typeEntry())->originator(); else if (arg->type()->isEnum()) enumTypeEntry = arg->type()->typeEntry(); if (enumTypeEntry) diff --git a/sources/shiboken2/generator/shiboken2/headergenerator.h b/sources/shiboken2/generator/shiboken2/headergenerator.h index acf0448a7..821531aab 100644 --- a/sources/shiboken2/generator/shiboken2/headergenerator.h +++ b/sources/shiboken2/generator/shiboken2/headergenerator.h @@ -55,8 +55,8 @@ private: void writeSbkTypeFunction(QTextStream& s, const AbstractMetaEnum* cppEnum); void writeSbkTypeFunction(QTextStream& s, const AbstractMetaClass* cppClass); void writeSbkTypeFunction(QTextStream &s, const AbstractMetaType *metaType); - void writeTypeIndexDefineLine(QTextStream& s, const TypeEntry* typeEntry); - void writeTypeIndexDefine(QTextStream& s, const AbstractMetaClass* metaClass); + void writeTypeIndexValueLine(QTextStream& s, const TypeEntry* typeEntry); + void writeTypeIndexValueLines(QTextStream& s, const AbstractMetaClass* metaClass); void writeProtectedEnumSurrogate(QTextStream& s, const AbstractMetaEnum* cppEnum); void writeInheritedOverloads(QTextStream& s); diff --git a/sources/shiboken2/generator/shiboken2/overloaddata.cpp b/sources/shiboken2/generator/shiboken2/overloaddata.cpp index 73198ba12..a95603754 100644 --- a/sources/shiboken2/generator/shiboken2/overloaddata.cpp +++ b/sources/shiboken2/generator/shiboken2/overloaddata.cpp @@ -94,8 +94,6 @@ static bool typesAreEqual(const AbstractMetaType* typeA, const AbstractMetaType* */ struct OverloadSortData { - OverloadSortData() : counter(0) {} - /** * Adds a typeName into the type map without associating it with * a OverloadData. This is done to express type dependencies that could @@ -121,7 +119,7 @@ struct OverloadSortData int lastProcessedItemId() { return counter - 1; } - int counter; + int counter = 0; QHash<QString, int> map; // typeName -> id QHash<int, OverloadData*> reverseMap; // id -> OverloadData; }; @@ -149,11 +147,11 @@ static QString getImplicitConversionTypeName(const AbstractMetaType* containerTy for (const AbstractMetaType *otherType : instantiations) types << (otherType == instantiation ? impConv : getTypeName(otherType)); - const ContainerTypeEntry* containerTypeEntry = dynamic_cast<const ContainerTypeEntry*>(containerType->typeEntry()); - return containerTypeEntry->qualifiedCppName() + QLatin1Char('<') + return containerType->typeEntry()->qualifiedCppName() + QLatin1Char('<') + types.join(QLatin1String(", ")) + QLatin1String(" >"); } +// overloaddata.cpp static QString msgCyclicDependency(const QString &funcName, const QString &graphName, const OverloadData::MetaFunctionList &involvedConversions) { @@ -499,7 +497,8 @@ OverloadData::OverloadData(const AbstractMetaFunctionList& overloads, const Shib OverloadData::OverloadData(OverloadData* headOverloadData, const AbstractMetaFunction* func, const AbstractMetaType* argType, int argPos) : m_minArgs(256), m_maxArgs(0), m_argPos(argPos), m_argType(argType), - m_headOverloadData(headOverloadData), m_previousOverloadData(0) + m_headOverloadData(headOverloadData), m_previousOverloadData(nullptr), + m_generator(nullptr) { if (func) this->addOverload(func); @@ -808,8 +807,7 @@ QPair<int, int> OverloadData::getMinMaxArguments(const AbstractMetaFunctionList& { int minArgs = 10000; int maxArgs = 0; - for (int i = 0; i < overloads.size(); i++) { - const AbstractMetaFunction* func = overloads[i]; + for (const AbstractMetaFunction *func : overloads) { int origNumArgs = func->arguments().size(); int removed = numberOfRemovedArguments(func); int numArgs = origNumArgs - removed; @@ -825,7 +823,7 @@ QPair<int, int> OverloadData::getMinMaxArguments(const AbstractMetaFunctionList& minArgs = fixedArgIndex; } } - return QPair<int, int>(minArgs, maxArgs); + return {minArgs, maxArgs}; } bool OverloadData::isSingleArgument(const AbstractMetaFunctionList& overloads) @@ -840,7 +838,7 @@ bool OverloadData::isSingleArgument(const AbstractMetaFunctionList& overloads) return singleArgument; } -void OverloadData::dumpGraph(QString filename) const +void OverloadData::dumpGraph(const QString &filename) const { QFile file(filename); if (file.open(QFile::WriteOnly)) { diff --git a/sources/shiboken2/generator/shiboken2/overloaddata.h b/sources/shiboken2/generator/shiboken2/overloaddata.h index 435c19aa2..4759ca9c3 100644 --- a/sources/shiboken2/generator/shiboken2/overloaddata.h +++ b/sources/shiboken2/generator/shiboken2/overloaddata.h @@ -114,7 +114,7 @@ public: /// Returns true if all overloads have no more than one argument. static bool isSingleArgument(const AbstractMetaFunctionList& overloads); - void dumpGraph(QString filename) const; + void dumpGraph(const QString &filename) const; QString dumpGraph() const; bool hasArgumentTypeReplace() const; diff --git a/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp b/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp index 80096cbf2..b9eea7529 100644 --- a/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp +++ b/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp @@ -28,9 +28,11 @@ #include "shibokengenerator.h" #include <abstractmetalang.h> +#include <messages.h> #include "overloaddata.h" #include <reporthandler.h> #include <typedatabase.h> +#include <abstractmetabuilder.h> #include <iostream> #include <QtCore/QDir> @@ -39,13 +41,29 @@ #include <limits> #include <memory> -#define NULL_VALUE "NULL" -#define AVOID_PROTECTED_HACK "avoid-protected-hack" -#define PARENT_CTOR_HEURISTIC "enable-parent-ctor-heuristic" -#define RETURN_VALUE_HEURISTIC "enable-return-value-heuristic" -#define ENABLE_PYSIDE_EXTENSIONS "enable-pyside-extensions" -#define DISABLE_VERBOSE_ERROR_MESSAGES "disable-verbose-error-messages" -#define USE_ISNULL_AS_NB_NONZERO "use-isnull-as-nb_nonzero" +static const char NULL_VALUE[] = "NULL"; +static const char AVOID_PROTECTED_HACK[] = "avoid-protected-hack"; +static const char PARENT_CTOR_HEURISTIC[] = "enable-parent-ctor-heuristic"; +static const char RETURN_VALUE_HEURISTIC[] = "enable-return-value-heuristic"; +static const char ENABLE_PYSIDE_EXTENSIONS[] = "enable-pyside-extensions"; +static const char DISABLE_VERBOSE_ERROR_MESSAGES[] = "disable-verbose-error-messages"; +static const char USE_ISNULL_AS_NB_NONZERO[] = "use-isnull-as-nb_nonzero"; + +const char *CPP_ARG = "cppArg"; +const char *CPP_ARG_REMOVED = "removed_cppArg"; +const char *CPP_RETURN_VAR = "cppResult"; +const char *CPP_SELF_VAR = "cppSelf"; +const char *PYTHON_ARG = "pyArg"; +const char *PYTHON_ARGS = "pyArgs"; +const char *PYTHON_OVERRIDE_VAR = "pyOverride"; +const char *PYTHON_RETURN_VAR = "pyResult"; +const char *PYTHON_TO_CPP_VAR = "pythonToCpp"; +const char *SMART_POINTER_GETTER = "kSmartPointerGetter"; + +const char *CONV_RULE_OUT_VAR_SUFFIX = "_out"; +const char *BEGIN_ALLOW_THREADS = + "PyThreadState* _save = PyEval_SaveThread(); // Py_BEGIN_ALLOW_THREADS"; +const char *END_ALLOW_THREADS = "PyEval_RestoreThread(_save); // Py_END_ALLOW_THREADS"; //static void dumpFunction(AbstractMetaFunctionList lst); @@ -100,7 +118,7 @@ static QString resolveScopePrefix(const AbstractMetaEnum *metaEnum, return resolveScopePrefix(parts, value); } -ShibokenGenerator::ShibokenGenerator() : Generator() +ShibokenGenerator::ShibokenGenerator() { if (m_pythonPrimitiveTypeName.isEmpty()) ShibokenGenerator::initPrimitiveTypesCorrespondences(); @@ -117,17 +135,19 @@ ShibokenGenerator::ShibokenGenerator() : Generator() m_typeSystemConvName[TypeSystemIsConvertibleFunction] = QLatin1String("isConvertible"); m_typeSystemConvName[TypeSystemToCppFunction] = QLatin1String("toCpp"); m_typeSystemConvName[TypeSystemToPythonFunction] = QLatin1String("toPython"); + + const char CHECKTYPE_REGEX[] = R"(%CHECKTYPE\[([^\[]*)\]\()"; + const char ISCONVERTIBLE_REGEX[] = R"(%ISCONVERTIBLE\[([^\[]*)\]\()"; + const char CONVERTTOPYTHON_REGEX[] = R"(%CONVERTTOPYTHON\[([^\[]*)\]\()"; + const char CONVERTTOCPP_REGEX[] = + R"((\*?%?[a-zA-Z_][\w\.]*(?:\[[^\[^<^>]+\])*)(?:\s+)=(?:\s+)%CONVERTTOCPP\[([^\[]*)\]\()"; m_typeSystemConvRegEx[TypeSystemCheckFunction] = QRegularExpression(QLatin1String(CHECKTYPE_REGEX)); m_typeSystemConvRegEx[TypeSystemIsConvertibleFunction] = QRegularExpression(QLatin1String(ISCONVERTIBLE_REGEX)); m_typeSystemConvRegEx[TypeSystemToPythonFunction] = QRegularExpression(QLatin1String(CONVERTTOPYTHON_REGEX)); m_typeSystemConvRegEx[TypeSystemToCppFunction] = QRegularExpression(QLatin1String(CONVERTTOCPP_REGEX)); } -ShibokenGenerator::~ShibokenGenerator() -{ - // TODO-CONVERTER: it must be caching types that were not created here. - //qDeleteAll(m_metaTypeFromStringCache.values()); -} +ShibokenGenerator::~ShibokenGenerator() = default; void ShibokenGenerator::clearTpFuncs() { @@ -277,7 +297,7 @@ bool ShibokenGenerator::shouldGenerateCppWrapper(const AbstractMetaClass* metaCl for (const AbstractMetaFunction *func : funcs) { if (!func->isProtected() || func->isSignal() || func->isModifiedRemoved()) continue; - else if (func->isOperatorOverload()) + if (func->isOperatorOverload()) protectedOperators++; else protectedFunctions++; @@ -332,9 +352,8 @@ QString ShibokenGenerator::wrapperName(const AbstractMetaClass* metaClass) const result += QLatin1String("Wrapper"); return result; - } else { - return metaClass->qualifiedCppName(); } + return metaClass->qualifiedCppName(); } QString ShibokenGenerator::wrapperName(const AbstractMetaType *metaType) const @@ -342,7 +361,19 @@ QString ShibokenGenerator::wrapperName(const AbstractMetaType *metaType) const return metaType->cppSignature(); } -QString ShibokenGenerator::fullPythonFunctionName(const AbstractMetaFunction* func) +QString ShibokenGenerator::fullPythonClassName(const AbstractMetaClass *metaClass) +{ + QString fullClassName = metaClass->name(); + const AbstractMetaClass *enclosing = metaClass->enclosingClass(); + while (enclosing) { + fullClassName.prepend(enclosing->name() + QLatin1Char('.')); + enclosing = enclosing->enclosingClass(); + } + fullClassName.prepend(packageName() + QLatin1Char('.')); + return fullClassName; +} + +QString ShibokenGenerator::fullPythonFunctionName(const AbstractMetaFunction *func) //WS { QString funcName; if (func->isOperatorOverload()) @@ -350,11 +381,11 @@ QString ShibokenGenerator::fullPythonFunctionName(const AbstractMetaFunction* fu else funcName = func->name(); if (func->ownerClass()) { - QString fullName = func->ownerClass()->fullName(); + QString fullClassName = fullPythonClassName(func->ownerClass()); if (func->isConstructor()) - funcName = fullName; + funcName = fullClassName; else - funcName.prepend(fullName + QLatin1Char('.')); + funcName.prepend(fullClassName + QLatin1Char('.')); } else { funcName = packageName() + QLatin1Char('.') + func->name(); @@ -434,7 +465,8 @@ QString ShibokenGenerator::cpythonSetterFunctionName(const AbstractMetaField* me return QStringLiteral("%1_set_%2").arg(cpythonBaseName(metaField->enclosingClass()), metaField->name()); } -static QString cpythonEnumFlagsName(QString moduleName, QString qualifiedCppName) +static QString cpythonEnumFlagsName(const QString &moduleName, + const QString &qualifiedCppName) { QString result = QStringLiteral("Sbk%1_%2").arg(moduleName, qualifiedCppName); result.replace(QLatin1String("::"), QLatin1String("_")); @@ -570,7 +602,7 @@ QString ShibokenGenerator::guessScopeForDefaultValue(const AbstractMetaFunction fieldName.prepend(prefix); prefix.clear(); } else { - fieldName.prepend(QLatin1String(CPP_SELF_VAR "->")); + fieldName.prepend(QLatin1String(CPP_SELF_VAR) + QLatin1String("->")); } value.replace(match.captured(1), fieldName); break; @@ -616,12 +648,14 @@ QString ShibokenGenerator::cpythonSpecialCastFunctionName(const AbstractMetaClas return cpythonBaseName(metaClass->typeEntry()) + QLatin1String("SpecialCastFunction"); } -QString ShibokenGenerator::cpythonWrapperCPtr(const AbstractMetaClass* metaClass, QString argName) +QString ShibokenGenerator::cpythonWrapperCPtr(const AbstractMetaClass* metaClass, + const QString &argName) { return cpythonWrapperCPtr(metaClass->typeEntry(), argName); } -QString ShibokenGenerator::cpythonWrapperCPtr(const AbstractMetaType *metaType, QString argName) +QString ShibokenGenerator::cpythonWrapperCPtr(const AbstractMetaType *metaType, + const QString &argName) { if (!ShibokenGenerator::isWrapperType(metaType->typeEntry())) return QString(); @@ -630,7 +664,8 @@ QString ShibokenGenerator::cpythonWrapperCPtr(const AbstractMetaType *metaType, + QLatin1String(", reinterpret_cast<SbkObject *>(") + argName + QLatin1String(")))"); } -QString ShibokenGenerator::cpythonWrapperCPtr(const TypeEntry* type, QString argName) +QString ShibokenGenerator::cpythonWrapperCPtr(const TypeEntry* type, + const QString &argName) { if (!ShibokenGenerator::isWrapperType(type)) return QString(); @@ -706,7 +741,8 @@ QString ShibokenGenerator::getFormatUnitString(const AbstractMetaFunction* func, || arg->type()->referenceType() == LValueReference) { result += QLatin1Char(objType); } else if (arg->type()->isPrimitive()) { - const PrimitiveTypeEntry* ptype = (const PrimitiveTypeEntry*) arg->type()->typeEntry(); + const PrimitiveTypeEntry *ptype = + static_cast<const PrimitiveTypeEntry *>(arg->type()->typeEntry()); if (ptype->basicReferencedTypeEntry()) ptype = ptype->basicReferencedTypeEntry(); if (m_formatUnits.contains(ptype->name())) @@ -745,7 +781,7 @@ QString ShibokenGenerator::cpythonBaseName(const TypeEntry* type) if (ShibokenGenerator::isWrapperType(type) || type->isNamespace()) { // && type->referenceType() == NoReference) { baseName = QLatin1String("Sbk_") + type->name(); } else if (type->isPrimitive()) { - const PrimitiveTypeEntry* ptype = (const PrimitiveTypeEntry*) type; + const PrimitiveTypeEntry *ptype = static_cast<const PrimitiveTypeEntry *>(type); while (ptype->basicReferencedTypeEntry()) ptype = ptype->basicReferencedTypeEntry(); if (ptype->targetLangApiName() == ptype->name()) @@ -753,11 +789,11 @@ QString ShibokenGenerator::cpythonBaseName(const TypeEntry* type) else baseName = ptype->targetLangApiName(); } else if (type->isEnum()) { - baseName = cpythonEnumName((const EnumTypeEntry*) type); + baseName = cpythonEnumName(static_cast<const EnumTypeEntry *>(type)); } else if (type->isFlags()) { - baseName = cpythonFlagsName((const FlagsTypeEntry*) type); + baseName = cpythonFlagsName(static_cast<const FlagsTypeEntry *>(type)); } else if (type->isContainer()) { - const ContainerTypeEntry* ctype = (const ContainerTypeEntry*) type; + const ContainerTypeEntry *ctype = static_cast<const ContainerTypeEntry *>(type); switch (ctype->type()) { case ContainerTypeEntry::ListContainer: case ContainerTypeEntry::StringListContainer: @@ -858,14 +894,6 @@ QString ShibokenGenerator::cpythonTypeNameExt(const AbstractMetaType* type) + getTypeIndexVariableName(type) + QLatin1Char(']'); } -static QString msgUnknownOperator(const AbstractMetaFunction* func) -{ - QString result = QLatin1String("Unknown operator: \"") + func->originalName() + QLatin1Char('"'); - if (const AbstractMetaClass *c = func->implementingClass()) - result += QLatin1String(" in class: ") + c->name(); - return result; -} - static inline QString unknownOperator() { return QStringLiteral("__UNKNOWN_OPERATOR__"); } QString ShibokenGenerator::fixedCppTypeName(const CustomConversion::TargetToNativeConversion* toNative) @@ -925,7 +953,7 @@ QString ShibokenGenerator::pythonPrimitiveTypeName(const PrimitiveTypeEntry* typ return pythonPrimitiveTypeName(type->name()); } -QString ShibokenGenerator::pythonOperatorFunctionName(QString cppOpFuncName) +QString ShibokenGenerator::pythonOperatorFunctionName(const QString &cppOpFuncName) { QString value = m_pythonOperators.value(cppOpFuncName); if (value.isEmpty()) @@ -953,7 +981,7 @@ QString ShibokenGenerator::pythonOperatorFunctionName(const AbstractMetaFunction return op; } -QString ShibokenGenerator::pythonRichCompareOperatorId(QString cppOpFuncName) +QString ShibokenGenerator::pythonRichCompareOperatorId(const QString &cppOpFuncName) { return QLatin1String("Py_") + m_pythonOperators.value(cppOpFuncName).toUpper(); } @@ -963,7 +991,7 @@ QString ShibokenGenerator::pythonRichCompareOperatorId(const AbstractMetaFunctio return pythonRichCompareOperatorId(func->originalName()); } -bool ShibokenGenerator::isNumber(QString cpythonApiName) +bool ShibokenGenerator::isNumber(const QString &cpythonApiName) { return cpythonApiName == QLatin1String("PyInt") || cpythonApiName == QLatin1String("PyFloat") @@ -975,7 +1003,7 @@ bool ShibokenGenerator::isNumber(const TypeEntry* type) { if (!type->isPrimitive()) return false; - return isNumber(pythonPrimitiveTypeName((const PrimitiveTypeEntry*) type)); + return isNumber(pythonPrimitiveTypeName(static_cast<const PrimitiveTypeEntry *>(type))); } bool ShibokenGenerator::isNumber(const AbstractMetaType* type) @@ -987,7 +1015,8 @@ bool ShibokenGenerator::isPyInt(const TypeEntry* type) { if (!type->isPrimitive()) return false; - return pythonPrimitiveTypeName((const PrimitiveTypeEntry*) type) == QLatin1String("PyInt"); + return pythonPrimitiveTypeName(static_cast<const PrimitiveTypeEntry *>(type)) + == QLatin1String("PyInt"); } bool ShibokenGenerator::isPyInt(const AbstractMetaType* type) @@ -998,7 +1027,7 @@ bool ShibokenGenerator::isPyInt(const AbstractMetaType* type) bool ShibokenGenerator::isWrapperType(const TypeEntry* type) { if (type->isComplex()) - return ShibokenGenerator::isWrapperType((const ComplexTypeEntry*)type); + return ShibokenGenerator::isWrapperType(static_cast<const ComplexTypeEntry *>(type)); return type->isObject() || type->isValue() || type->isSmartPointer(); } bool ShibokenGenerator::isWrapperType(const ComplexTypeEntry* type) @@ -1052,7 +1081,7 @@ bool ShibokenGenerator::isUserPrimitive(const TypeEntry* type) { if (!type->isPrimitive()) return false; - const PrimitiveTypeEntry* trueType = (const PrimitiveTypeEntry*) type; + const PrimitiveTypeEntry *trueType = static_cast<const PrimitiveTypeEntry *>(type); if (trueType->basicReferencedTypeEntry()) trueType = trueType->basicReferencedTypeEntry(); return trueType->isPrimitive() && !trueType->isCppPrimitive() @@ -1072,7 +1101,7 @@ bool ShibokenGenerator::isCppPrimitive(const TypeEntry* type) return true; if (!type->isPrimitive()) return false; - const PrimitiveTypeEntry* trueType = (const PrimitiveTypeEntry*) type; + const PrimitiveTypeEntry *trueType = static_cast<const PrimitiveTypeEntry *>(type); if (trueType->basicReferencedTypeEntry()) trueType = trueType->basicReferencedTypeEntry(); return trueType->qualifiedCppName() == QLatin1String("std::string"); @@ -1125,9 +1154,11 @@ QString ShibokenGenerator::cpythonCheckFunction(const AbstractMetaType* metaType if (isVoidPointer(metaType)) return QLatin1String("PyObject_Check"); return cpythonCheckFunction(metaType->typeEntry(), genericNumberType); - } else if (metaType->typeEntry()->isContainer()) { + } + if (metaType->typeEntry()->isContainer()) { QString typeCheck = QLatin1String("Shiboken::Conversions::"); - ContainerTypeEntry::Type type = ((const ContainerTypeEntry*)metaType->typeEntry())->type(); + ContainerTypeEntry::Type type = + static_cast<const ContainerTypeEntry *>(metaType->typeEntry())->type(); if (type == ContainerTypeEntry::ListContainer || type == ContainerTypeEntry::StringListContainer || type == ContainerTypeEntry::LinkedListContainer @@ -1154,8 +1185,8 @@ QString ShibokenGenerator::cpythonCheckFunction(const AbstractMetaType* metaType const AbstractMetaType* firstType = metaType->instantiations().constFirst(); const AbstractMetaType* secondType = metaType->instantiations().constLast(); if (isPointerToWrapperType(firstType) && isPointerToWrapperType(secondType)) { - typeCheck += QString::fromLatin1("check%1Types(%2, %3, ").arg(pyType) - .arg(cpythonTypeNameExt(firstType), cpythonTypeNameExt(secondType)); + typeCheck += QString::fromLatin1("check%1Types(%2, %3, ") + .arg(pyType, cpythonTypeNameExt(firstType), cpythonTypeNameExt(secondType)); } else { typeCheck += QString::fromLatin1("convertible%1Types(%2, %3, %4, %5, ") .arg(pyType, converterObject(firstType), @@ -1182,8 +1213,10 @@ QString ShibokenGenerator::cpythonCheckFunction(const TypeEntry* type, bool gene if (type->isEnum() || type->isFlags() || isWrapperType(type)) return QString::fromLatin1("SbkObject_TypeCheck(%1, ").arg(cpythonTypeNameExt(type)); - else if (isCppPrimitive(type)) - return pythonPrimitiveTypeName((const PrimitiveTypeEntry*)type) + QLatin1String("_Check"); + if (isCppPrimitive(type)) { + return pythonPrimitiveTypeName(static_cast<const PrimitiveTypeEntry *>(type)) + + QLatin1String("_Check"); + } QString typeCheck; if (type->targetLangApiName() == type->name()) typeCheck = cpythonIsConvertibleFunction(type); @@ -1392,7 +1425,7 @@ void ShibokenGenerator::writeFunctionArguments(QTextStream &s, if (options & Generator::WriteSelf) { s << func->implementingClass()->name() << '&'; if (!(options & SkipName)) - s << " " PYTHON_SELF_VAR; + s << " self"; } int argUsed = 0; @@ -1412,13 +1445,12 @@ QString ShibokenGenerator::functionReturnType(const AbstractMetaFunction* func, QString modifiedReturnType = QString(func->typeReplaced(0)); if (!modifiedReturnType.isNull() && !(options & OriginalTypeDescription)) return modifiedReturnType; - else - return translateType(func->type(), func->implementingClass(), options); + return translateType(func->type(), func->implementingClass(), options); } QString ShibokenGenerator::functionSignature(const AbstractMetaFunction *func, - QString prepend, - QString append, + const QString &prepend, + const QString &append, Options options, int /* argCount */) const { @@ -1619,8 +1651,9 @@ ShibokenGenerator::ArgumentVarReplacementList ShibokenGenerator::getArgumentRepl QString argValue; if (language == TypeSystem::TargetLangCode) { bool hasConversionRule = !func->conversionRule(convLang, i+1).isEmpty(); - bool argRemoved = func->argumentRemoved(i+1); - removed = removed + (int) argRemoved; + const bool argRemoved = func->argumentRemoved(i+1); + if (argRemoved) + ++removed; if (argRemoved && hasConversionRule) argValue = arg->name() + QLatin1String(CONV_RULE_OUT_VAR_SUFFIX); else if (argRemoved || (lastArg && arg->argumentIndex() > lastArg->argumentIndex())) @@ -1636,8 +1669,7 @@ ShibokenGenerator::ArgumentVarReplacementList ShibokenGenerator::getArgumentRepl } if (type->typeEntry()->isCustom()) { argValue = usePyArgs - ? QString::fromLatin1(PYTHON_ARGS "[%1]").arg(argPos) - : QLatin1String(PYTHON_ARG); + ? pythonArgsAt(argPos) : QLatin1String(PYTHON_ARG); } else { argValue = hasConversionRule ? arg->name() + QLatin1String(CONV_RULE_OUT_VAR_SUFFIX) @@ -1673,17 +1705,6 @@ void ShibokenGenerator::writeCodeSnips(QTextStream& s, s << INDENT << "// End of code injection" << endl; } -static QString msgWrongIndex(const char *varName, const QString &capture, const AbstractMetaFunction *func) -{ - QString result; - QTextStream str(&result); - str << "Wrong index for " << varName << " variable (" << capture << ") on "; - if (const AbstractMetaClass *c = func->implementingClass()) - str << c->name() << "::"; - str << func->signature(); - return result; -} - void ShibokenGenerator::writeCodeSnips(QTextStream& s, const CodeSnipList& codeSnips, TypeSystem::CodeSnipPosition position, @@ -1712,7 +1733,7 @@ void ShibokenGenerator::writeCodeSnips(QTextStream& s, Q_ASSERT(pyArgsRegex.isValid()); if (language == TypeSystem::TargetLangCode) { if (usePyArgs) { - code.replace(pyArgsRegex, QLatin1String(PYTHON_ARGS"[\\1-1]")); + code.replace(pyArgsRegex, QLatin1String(PYTHON_ARGS) + QLatin1String("[\\1-1]")); } else { static const QRegularExpression pyArgsRegexCheck(QStringLiteral("%PYARG_([2-9]+)")); Q_ASSERT(pyArgsRegexCheck.isValid()); @@ -1729,8 +1750,10 @@ void ShibokenGenerator::writeCodeSnips(QTextStream& s, // Python argument on the binding virtual method. static const QRegularExpression pyArgsAttributionRegex(QStringLiteral("%PYARG_(\\d+)\\s*=[^=]\\s*([^;]+)")); Q_ASSERT(pyArgsAttributionRegex.isValid()); - code.replace(pyArgsAttributionRegex, QLatin1String("PyTuple_SET_ITEM(" PYTHON_ARGS ", \\1-1, \\2)")); - code.replace(pyArgsRegex, QLatin1String("PyTuple_GET_ITEM(" PYTHON_ARGS ", \\1-1)")); + code.replace(pyArgsAttributionRegex, QLatin1String("PyTuple_SET_ITEM(") + + QLatin1String(PYTHON_ARGS) + QLatin1String(", \\1-1, \\2)")); + code.replace(pyArgsRegex, QLatin1String("PyTuple_GET_ITEM(") + + QLatin1String(PYTHON_ARGS) + QLatin1String(", \\1-1)")); } // Replace %ARG#_TYPE variables. @@ -1763,7 +1786,8 @@ void ShibokenGenerator::writeCodeSnips(QTextStream& s, } // Replace template variable for self Python object. - QString pySelf = (language == TypeSystem::NativeCode) ? QLatin1String("pySelf") : QLatin1String(PYTHON_SELF_VAR); + QString pySelf = language == TypeSystem::NativeCode + ? QLatin1String("pySelf") : QLatin1String("self"); code.replace(QLatin1String("%PYSELF"), pySelf); // Replace template variable for a pointer to C++ of this object. @@ -1960,16 +1984,6 @@ static QString getConverterTypeSystemVariableArgument(const QString& code, int p } typedef QPair<QString, QString> StringPair; -static QString msgCannotFindType(const QString &type, const QString &variable, - const QString &why) -{ - QString result; - QTextStream(&result) << "Could not find type '" - << type << "' for use in '" << variable << "' conversion: " << why - << "\nMake sure to use the full C++ name, e.g. 'Namespace::Class'."; - return result; -} - void ShibokenGenerator::replaceConverterTypeSystemVariable(TypeSystemConverterVariable converterVariable, QString& code) { QVector<StringPair> replacements; @@ -1978,7 +1992,7 @@ void ShibokenGenerator::replaceConverterTypeSystemVariable(TypeSystemConverterVa const QRegularExpressionMatch match = rit.next(); const QStringList list = match.capturedTexts(); QString conversionString = list.constFirst(); - QString conversionTypeName = list.constLast(); + const QString &conversionTypeName = list.constLast(); QString message; const AbstractMetaType *conversionType = buildAbstractMetaTypeFromString(conversionTypeName, &message); if (!conversionType) { @@ -2002,8 +2016,8 @@ void ShibokenGenerator::replaceConverterTypeSystemVariable(TypeSystemConverterVa QString varName = list.at(1).trimmed(); if (!varType.isEmpty()) { if (varType != conversionType->cppSignature()) { - qFatal(qPrintable(QString::fromLatin1("Types of receiver variable ('%1') and %CONVERTTOCPP type system variable ('%2') differ.") - .arg(varType, conversionType->cppSignature())), NULL); + qFatal("Types of receiver variable ('%s') and %%CONVERTTOCPP type system variable ('%s') differ.", + qPrintable(varType), qPrintable(conversionType->cppSignature())); } c << getFullTypeName(conversionType) << ' ' << varName; writeMinimalConstructorExpression(c, conversionType); @@ -2032,18 +2046,21 @@ void ShibokenGenerator::replaceConverterTypeSystemVariable(TypeSystemConverterVa c << '('; break; } + Q_FALLTHROUGH(); case TypeSystemIsConvertibleFunction: if (conversion.isEmpty()) conversion = cpythonIsConvertibleFunction(conversionType); + Q_FALLTHROUGH(); case TypeSystemToPythonFunction: if (conversion.isEmpty()) conversion = cpythonToPythonConversionFunction(conversionType); + Q_FALLTHROUGH(); default: { QString arg = getConverterTypeSystemVariableArgument(code, match.capturedEnd()); conversionString += arg; if (converterVariable == TypeSystemToPythonFunction && !isVariable(arg)) { - qFatal(qPrintable(QString::fromLatin1("Only variables are acceptable as argument to %%CONVERTTOPYTHON type system variable on code snippet: '%1'") - .arg(code)), NULL); + qFatal("Only variables are acceptable as argument to %%CONVERTTOPYTHON type system variable on code snippet: '%s'", + qPrintable(code)); } if (conversion.contains(QLatin1String("%in"))) { conversion.prepend(QLatin1Char('(')); @@ -2172,9 +2189,7 @@ bool ShibokenGenerator::classNeedsSetattroFunction(const AbstractMetaClass *meta { if (!metaClass) return false; - if (metaClass->typeEntry()->isSmartPointer()) - return true; - return false; + return metaClass->typeEntry()->isSmartPointer(); } AbstractMetaFunctionList ShibokenGenerator::getMethodsWithBothStaticAndNonStaticMethods(const AbstractMetaClass* metaClass) @@ -2244,9 +2259,7 @@ AbstractMetaClassList ShibokenGenerator::getAllAncestors(const AbstractMetaClass QString ShibokenGenerator::getModuleHeaderFileName(const QString& moduleName) const { - QString result = moduleName.isEmpty() ? packageName() : moduleName; - result.replace(QLatin1Char('.'), QLatin1Char('_')); - return result.toLower() + QLatin1String("_python.h"); + return moduleCppPrefix(moduleName).toLower() + QLatin1String("_python.h"); } bool ShibokenGenerator::isCopyable(const AbstractMetaClass *metaClass) @@ -2254,12 +2267,10 @@ bool ShibokenGenerator::isCopyable(const AbstractMetaClass *metaClass) { if (metaClass->isNamespace() || isObjectType(metaClass)) return false; - else if (metaClass->typeEntry()->copyable() == ComplexTypeEntry::Unknown) + if (metaClass->typeEntry()->copyable() == ComplexTypeEntry::Unknown) return metaClass->hasCloneOperator(); - else - return (metaClass->typeEntry()->copyable() == ComplexTypeEntry::CopyableSet); - return false; + return metaClass->typeEntry()->copyable() == ComplexTypeEntry::CopyableSet; } AbstractMetaType *ShibokenGenerator::buildAbstractMetaTypeFromString(QString typeSignature, @@ -2269,110 +2280,18 @@ AbstractMetaType *ShibokenGenerator::buildAbstractMetaTypeFromString(QString typ if (typeSignature.startsWith(QLatin1String("::"))) typeSignature.remove(0, 2); - if (m_metaTypeFromStringCache.contains(typeSignature)) - return m_metaTypeFromStringCache.value(typeSignature); - - QString typeString = typeSignature; - bool isConst = typeString.startsWith(QLatin1String("const ")); - if (isConst) - typeString.remove(0, sizeof("const ") / sizeof(char) - 1); - - ReferenceType refType = NoReference; - if (typeString.endsWith(QLatin1String("&&"))) { - refType = RValueReference; - typeString.chop(2); - typeString = typeString.trimmed(); - } else if (typeString.endsWith(QLatin1Char('&'))) { - refType = LValueReference; - typeString.chop(1); - typeString = typeString.trimmed(); - } - - int indirections = 0; - while (typeString.endsWith(QLatin1Char('*'))) { - ++indirections; - typeString.chop(1); - typeString = typeString.trimmed(); - } - - if (typeString.startsWith(QLatin1String("::"))) - typeString.remove(0, 2); - - QString adjustedTypeName = typeString; - AbstractMetaTypeList instantiations; - int lpos = typeString.indexOf(QLatin1Char('<')); - if (lpos > -1) { - QStringList instantiatedTypes; - int rpos = typeString.lastIndexOf(QLatin1Char('>')); - if ((lpos != -1) && (rpos != -1)) { - QString type = typeString.mid(lpos + 1, rpos - lpos - 1); - int depth = 0; - int start = 0; - for (int i = 0; i < type.count(); ++i) { - if (type.at(i) == QLatin1Char('<')) { - ++depth; - } else if (type.at(i) == QLatin1Char('>')) { - --depth; - } else if (type.at(i) == QLatin1Char(',') && depth == 0) { - instantiatedTypes << type.mid(start, i - start).trimmed(); - start = i + 1; - } - } - instantiatedTypes << type.mid(start).trimmed(); - adjustedTypeName.truncate(lpos); - } - for (const QString &instantiatedType : qAsConst(instantiatedTypes)) { - AbstractMetaType *tmplArgType = buildAbstractMetaTypeFromString(instantiatedType); - if (!tmplArgType) { - if (errorMessage) { - QTextStream(errorMessage) << "Cannot find template type \"" - << instantiatedType << "\" for \"" << typeSignature << "\"."; - } - return nullptr; - } - instantiations.append(tmplArgType); - } - } - - TypeEntry *typeEntry = nullptr; - AbstractMetaType::TypeUsagePattern pattern = AbstractMetaType::InvalidPattern; - - if (instantiations.size() == 1 - && instantiations.at(0)->typeUsagePattern() == AbstractMetaType::EnumPattern - && adjustedTypeName == QLatin1String("QFlags")) { - pattern = AbstractMetaType::FlagsPattern; - typeEntry = TypeDatabase::instance()->findType(typeSignature); - } else { - typeEntry = TypeDatabase::instance()->findType(adjustedTypeName); - } - - if (!typeEntry) { - if (errorMessage) { - QTextStream(errorMessage) << "Cannot find type \"" << adjustedTypeName - << "\" for \"" << typeSignature << "\"."; + auto it = m_metaTypeFromStringCache.find(typeSignature); + if (it == m_metaTypeFromStringCache.end()) { + AbstractMetaType *metaType = + AbstractMetaBuilder::translateType(typeSignature, nullptr, true, errorMessage); + if (Q_UNLIKELY(!metaType)) { + if (errorMessage) + errorMessage->prepend(msgCannotBuildMetaType(typeSignature)); + return nullptr; } - return nullptr; + it = m_metaTypeFromStringCache.insert(typeSignature, metaType); } - - AbstractMetaType *metaType = new AbstractMetaType(); - metaType->setTypeEntry(typeEntry); - metaType->setIndirections(indirections); - metaType->setReferenceType(refType); - metaType->setConstant(isConst); - metaType->setTypeUsagePattern(AbstractMetaType::ContainerPattern); - switch (pattern) { - case AbstractMetaType::FlagsPattern: - metaType->setTypeUsagePattern(pattern); - break; - default: - metaType->setInstantiations(instantiations); - metaType->setTypeUsagePattern(AbstractMetaType::ContainerPattern); - metaType->decideUsagePattern(); - break; - } - - m_metaTypeFromStringCache.insert(typeSignature, metaType); - return metaType; + return it.value(); } AbstractMetaType* ShibokenGenerator::buildAbstractMetaTypeFromTypeEntry(const TypeEntry* typeEntry) @@ -2384,7 +2303,7 @@ AbstractMetaType* ShibokenGenerator::buildAbstractMetaTypeFromTypeEntry(const Ty return m_metaTypeFromStringCache.value(typeName); AbstractMetaType* metaType = new AbstractMetaType; metaType->setTypeEntry(typeEntry); - metaType->setIndirections(0); + metaType->clearIndirections(); metaType->setReferenceType(NoReference); metaType->setConstant(false); metaType->decideUsagePattern(); @@ -2449,7 +2368,7 @@ AbstractMetaFunctionList ShibokenGenerator::getInheritedOverloads(const Abstract { AbstractMetaFunctionList results; AbstractMetaClass* basis; - if (func->ownerClass() && (basis = func->ownerClass()->baseClass(), basis)) { + if (func->ownerClass() && (basis = func->ownerClass()->baseClass())) { for (; basis; basis = basis->baseClass()) { const AbstractMetaFunction* inFunc = basis->findFunction(func->name()); if (inFunc && !seen->contains(inFunc->minimalSignature())) { @@ -2509,6 +2428,23 @@ Generator::OptionDescriptions ShibokenGenerator::options() const "the value of boolean casts")); } +bool ShibokenGenerator::handleOption(const QString &key, const QString & /* value */) +{ + if (key == QLatin1String(PARENT_CTOR_HEURISTIC)) + return (m_useCtorHeuristic = true); + if (key == QLatin1String(ENABLE_PYSIDE_EXTENSIONS)) + return (m_usePySideExtensions = true); + if (key == QLatin1String(RETURN_VALUE_HEURISTIC)) + return (m_userReturnValueHeuristic = true); + if (key == QLatin1String(DISABLE_VERBOSE_ERROR_MESSAGES)) + return (m_verboseErrorMessagesDisabled = true); + if (key == QLatin1String(USE_ISNULL_AS_NB_NONZERO)) + return (m_useIsNullAsNbNonZero = true); + if (key == QLatin1String(AVOID_PROTECTED_HACK)) + return (m_avoidProtectedHack = true); + return false; +} + static void getCode(QStringList& code, const CodeSnipList& codeSnips) { for (const CodeSnip &snip : qAsConst(codeSnips)) @@ -2534,15 +2470,8 @@ static void getCode(QStringList& code, const TypeEntry* type) code.append(toNative->conversion()); } -bool ShibokenGenerator::doSetup(const QMap<QString, QString>& args) +bool ShibokenGenerator::doSetup() { - m_useCtorHeuristic = args.contains(QLatin1String(PARENT_CTOR_HEURISTIC)); - m_usePySideExtensions = args.contains(QLatin1String(ENABLE_PYSIDE_EXTENSIONS)); - m_userReturnValueHeuristic = args.contains(QLatin1String(RETURN_VALUE_HEURISTIC)); - m_verboseErrorMessagesDisabled = args.contains(QLatin1String(DISABLE_VERBOSE_ERROR_MESSAGES)); - m_useIsNullAsNbNonZero = args.contains(QLatin1String(USE_ISNULL_AS_NB_NONZERO)); - m_avoidProtectedHack = args.contains(QLatin1String(AVOID_PROTECTED_HACK)); - TypeDatabase* td = TypeDatabase::instance(); QStringList snips; const PrimitiveTypeEntryList &primitiveTypeList = primitiveTypes(); @@ -2554,7 +2483,11 @@ bool ShibokenGenerator::doSetup(const QMap<QString, QString>& args) const AbstractMetaClassList &classList = classes(); for (const AbstractMetaClass *metaClass : classList) getCode(snips, metaClass->typeEntry()); - getCode(snips, td->findType(packageName())); + + const TypeSystemTypeEntry *moduleEntry = td->findTypeSystemType(packageName()); + Q_ASSERT(moduleEntry); + getCode(snips, moduleEntry); + const FunctionGroupMap &functionGroups = getFunctionGroups(); for (FunctionGroupMapIt it = functionGroups.cbegin(), end = functionGroups.cend(); it != end; ++it) { for (AbstractMetaFunction *func : it.value()) @@ -2611,15 +2544,25 @@ bool ShibokenGenerator::avoidProtectedHack() const return m_avoidProtectedHack; } -QString ShibokenGenerator::cppApiVariableName(const QString& moduleName) const -{ - QString result = moduleName.isEmpty() ? ShibokenGenerator::packageName() : moduleName; +QString ShibokenGenerator::moduleCppPrefix(const QString& moduleName) const + { + QString result = moduleName.isEmpty() ? packageName() : moduleName; result.replace(QLatin1Char('.'), QLatin1Char('_')); - result.prepend(QLatin1String("Sbk")); - result.append(QLatin1String("Types")); return result; } +QString ShibokenGenerator::cppApiVariableName(const QString& moduleName) const +{ + return QLatin1String("Sbk") + moduleCppPrefix(moduleName) + + QLatin1String("Types"); +} + +QString ShibokenGenerator::pythonModuleObjectName(const QString& moduleName) const +{ + return QLatin1String("Sbk") + moduleCppPrefix(moduleName) + + QLatin1String("ModuleObject"); +} + QString ShibokenGenerator::convertersVariableName(const QString& moduleName) const { QString result = cppApiVariableName(moduleName); @@ -2639,35 +2582,50 @@ static QString processInstantiationsVariableName(const AbstractMetaType* type) } return res; } + +static void appendIndexSuffix(QString *s) +{ + if (!s->endsWith(QLatin1Char('_'))) + s->append(QLatin1Char('_')); + s->append(QStringLiteral("IDX")); +} + QString ShibokenGenerator::getTypeIndexVariableName(const AbstractMetaClass* metaClass, bool alternativeTemplateName) { if (alternativeTemplateName) { const AbstractMetaClass* templateBaseClass = metaClass->templateBaseClass(); if (!templateBaseClass) return QString(); - QString base = _fixedCppTypeName(templateBaseClass->typeEntry()->qualifiedCppName()).toUpper(); - QString instantiations; + QString result = QLatin1String("SBK_") + + _fixedCppTypeName(templateBaseClass->typeEntry()->qualifiedCppName()).toUpper(); const AbstractMetaTypeList &templateBaseClassInstantiations = metaClass->templateBaseClassInstantiations(); for (const AbstractMetaType *instantiation : templateBaseClassInstantiations) - instantiations += processInstantiationsVariableName(instantiation); - return QString::fromLatin1("SBK_%1%2_IDX").arg(base, instantiations); + result += processInstantiationsVariableName(instantiation); + appendIndexSuffix(&result); + return result; } return getTypeIndexVariableName(metaClass->typeEntry()); } QString ShibokenGenerator::getTypeIndexVariableName(const TypeEntry* type) { if (type->isCppPrimitive()) { - const PrimitiveTypeEntry* trueType = (const PrimitiveTypeEntry*) type; + const PrimitiveTypeEntry *trueType = static_cast<const PrimitiveTypeEntry*>(type); if (trueType->basicReferencedTypeEntry()) type = trueType->basicReferencedTypeEntry(); } - return QString::fromLatin1("SBK_%1_IDX").arg(_fixedCppTypeName(type->qualifiedCppName()).toUpper()); + QString result = QLatin1String("SBK_") + + _fixedCppTypeName(type->qualifiedCppName()).toUpper(); + appendIndexSuffix(&result); + return result; } QString ShibokenGenerator::getTypeIndexVariableName(const AbstractMetaType* type) { - return QString::fromLatin1("SBK%1%2_IDX") - .arg(type->typeEntry()->isContainer() ? QLatin1Char('_') + moduleName().toUpper() : QString(), - processInstantiationsVariableName(type)); + QString result = QLatin1String("SBK"); + if (type->typeEntry()->isContainer()) + result += QLatin1Char('_') + moduleName().toUpper(); + result += processInstantiationsVariableName(type); + appendIndexSuffix(&result); + return result; } bool ShibokenGenerator::verboseErrorMessagesDisabled() const @@ -2707,30 +2665,37 @@ QString ShibokenGenerator::getDefaultValue(const AbstractMetaFunction* func, co void ShibokenGenerator::writeMinimalConstructorExpression(QTextStream& s, const AbstractMetaType* type, const QString& defaultCtor) { - if (defaultCtor.isEmpty() && isCppPrimitive(type)) + if (!defaultCtor.isEmpty()) { + s << " = " << defaultCtor; + return; + } + if (isCppPrimitive(type)) return; - QString ctor = defaultCtor.isEmpty() ? minimalConstructor(type) : defaultCtor; - if (ctor.isEmpty()) { + const auto ctor = minimalConstructor(type); + if (ctor.isValid()) { + s << ctor.initialization(); + } else { const QString message = msgCouldNotFindMinimalConstructor(QLatin1String(__FUNCTION__), type->cppSignature()); qCWarning(lcShiboken()).noquote() << message; s << ";\n#error " << message << '\n'; - } else { - s << " = " << ctor; } } void ShibokenGenerator::writeMinimalConstructorExpression(QTextStream& s, const TypeEntry* type, const QString& defaultCtor) { - if (defaultCtor.isEmpty() && isCppPrimitive(type)) + if (!defaultCtor.isEmpty()) { + s << " = " << defaultCtor; + return; + } + if (isCppPrimitive(type)) return; - QString ctor = defaultCtor.isEmpty() ? minimalConstructor(type) : defaultCtor; - - if (ctor.isEmpty()) { + const auto ctor = minimalConstructor(type); + if (ctor.isValid()) { + s << ctor.initialization(); + } else { const QString message = msgCouldNotFindMinimalConstructor(QLatin1String(__FUNCTION__), type->qualifiedCppName()); qCWarning(lcShiboken()).noquote() << message; s << ";\n#error " << message << endl; - } else { - s << " = " << ctor; } } @@ -2738,7 +2703,7 @@ bool ShibokenGenerator::isCppIntegralPrimitive(const TypeEntry* type) { if (!type->isCppPrimitive()) return false; - const PrimitiveTypeEntry* trueType = (const PrimitiveTypeEntry*) type; + const PrimitiveTypeEntry *trueType = static_cast<const PrimitiveTypeEntry *>(type); if (trueType->basicReferencedTypeEntry()) trueType = trueType->basicReferencedTypeEntry(); QString typeName = trueType->qualifiedCppName(); @@ -2751,8 +2716,8 @@ bool ShibokenGenerator::isCppIntegralPrimitive(const AbstractMetaType* type) return isCppIntegralPrimitive(type->typeEntry()); } -QString ShibokenGenerator::msgCouldNotFindMinimalConstructor(const QString &where, const QString &type) +QString ShibokenGenerator::pythonArgsAt(int i) { - return where + QLatin1String(": Could not find a minimal constructor for type '") + type - + QLatin1String("'. This will result in a compilation error."); + return QLatin1String(PYTHON_ARGS) + QLatin1Char('[') + + QString::number(i) + QLatin1Char(']'); } diff --git a/sources/shiboken2/generator/shiboken2/shibokengenerator.h b/sources/shiboken2/generator/shiboken2/shibokengenerator.h index cb1bdd11f..60e31a99b 100644 --- a/sources/shiboken2/generator/shiboken2/shibokengenerator.h +++ b/sources/shiboken2/generator/shiboken2/shibokengenerator.h @@ -29,28 +29,20 @@ #ifndef SHIBOKENGENERATOR_H #define SHIBOKENGENERATOR_H -#define CONV_RULE_OUT_VAR_SUFFIX "_out" -#define CPP_ARG "cppArg" -#define CPP_ARG0 CPP_ARG"0" -#define CPP_ARG_REMOVED "removed_" CPP_ARG -#define CPP_RETURN_VAR "cppResult" -#define CPP_SELF_VAR "cppSelf" -#define PYTHON_ARG "pyArg" -#define PYTHON_ARGS PYTHON_ARG "s" -#define PYTHON_OVERRIDE_VAR "pyOverride" -#define PYTHON_RETURN_VAR "pyResult" -#define PYTHON_SELF_VAR "self" -#define THREAD_STATE_SAVER_VAR "threadStateSaver" -#define BEGIN_ALLOW_THREADS "PyThreadState* _save = PyEval_SaveThread(); // Py_BEGIN_ALLOW_THREADS" -#define END_ALLOW_THREADS "PyEval_RestoreThread(_save); // Py_END_ALLOW_THREADS" -#define PYTHON_TO_CPP_VAR "pythonToCpp" -#define SMART_POINTER_GETTER "kSmartPointerGetter" - -#define CHECKTYPE_REGEX "%CHECKTYPE\\[([^\\[]*)\\]\\(" -#define ISCONVERTIBLE_REGEX "%ISCONVERTIBLE\\[([^\\[]*)\\]\\(" -#define CONVERTTOPYTHON_REGEX "%CONVERTTOPYTHON\\[([^\\[]*)\\]\\(" -#define CONVERTTOCPP_REGEX "(\\*?%?[a-zA-Z_][\\w\\.]*(?:\\[[^\\[^<^>]+\\])*)"\ - "(?:\\s+)=(?:\\s+)%CONVERTTOCPP\\[([^\\[]*)\\]\\(" +extern const char *CPP_ARG; +extern const char *CPP_ARG_REMOVED; +extern const char *CPP_RETURN_VAR; +extern const char *CPP_SELF_VAR; +extern const char *PYTHON_ARG; +extern const char *PYTHON_ARGS; +extern const char *PYTHON_OVERRIDE_VAR; +extern const char *PYTHON_RETURN_VAR; +extern const char *PYTHON_TO_CPP_VAR; +extern const char *SMART_POINTER_GETTER; + +extern const char *CONV_RULE_OUT_VAR_SUFFIX; +extern const char *BEGIN_ALLOW_THREADS; +extern const char *END_ALLOW_THREADS; #include <generator.h> @@ -71,61 +63,20 @@ class ShibokenGenerator : public Generator { public: ShibokenGenerator(); - virtual ~ShibokenGenerator(); + ~ShibokenGenerator() override; - QString translateTypeForWrapperMethod(const AbstractMetaType* cType, - const AbstractMetaClass* context, Options opt = NoOption) const; + const char *name() const override { return "Shiboken"; } /** - * Returns a map with all functions grouped, the function name is used as key. - * Example of return value: { "foo" -> ["foo(int)", "foo(int, long)], "bar" -> "bar(double)"} - * \param scope Where to search for functions, null means all global functions. - */ - QMap<QString, AbstractMetaFunctionList> getFunctionGroups(const AbstractMetaClass* scope = 0); - - /** - * Returns all different inherited overloads of func. - * The function can be called multiple times without duplication. - * \param func the metafunction to be searched in subclasses. - * \param seen the function's minimal signatures already seen. - */ - AbstractMetaFunctionList getInheritedOverloads(const AbstractMetaFunction *func, QSet<QString> *seen); + * Helper function to find for argument default value + */ + static QString getDefaultValue(const AbstractMetaFunction* func, const AbstractMetaArgument* arg); - /** - * Returns all different inherited overloads of func, and includes func as well. - * The function can be called multiple times without duplication. - * \param func the metafunction to be searched in subclasses. - * \param seen the function's minimal signatures already seen. - */ - AbstractMetaFunctionList getFunctionAndInheritedOverloads(const AbstractMetaFunction *func, QSet<QString> *seen); + /// Returns a list of all ancestor classes for the given class. + AbstractMetaClassList getAllAncestors(const AbstractMetaClass* metaClass) const; - /** - * Returns all overloads for a function named \p functionName. - * \param scope scope used to search for overloads. - * \param functionName the function name. - */ - AbstractMetaFunctionList getFunctionOverloads(const AbstractMetaClass* scope, const QString& functionName); - /** - * Write a function argument in the C++ in the text stream \p s. - * This function just call \code s << argumentString(); \endcode - * \param s text stream used to write the output. - * \param func the current metafunction. - * \param argument metaargument information to be parsed. - * \param options some extra options. - */ - void writeArgument(QTextStream &s, - const AbstractMetaFunction* func, - const AbstractMetaArgument* argument, - Options options = NoOption) const; - /** - * Create a QString in the C++ format to an function argument. - * \param func the current metafunction. - * \param argument metaargument information to be parsed. - * \param options some extra options. - */ - QString argumentString(const AbstractMetaFunction* func, - const AbstractMetaArgument* argument, - Options options = NoOption) const; +protected: + bool doSetup() override; void writeArgumentNames(QTextStream &s, const AbstractMetaFunction* func, @@ -141,14 +92,21 @@ public: void writeFunctionArguments(QTextStream &s, const AbstractMetaFunction* func, Options options = NoOption) const override; - QString functionReturnType(const AbstractMetaFunction* func, Options options = NoOption) const; - /// Utility function for writeCodeSnips. - typedef QPair<const AbstractMetaArgument*, QString> ArgumentVarReplacementPair; - typedef QVector<ArgumentVarReplacementPair> ArgumentVarReplacementList; - ArgumentVarReplacementList getArgumentReplacement(const AbstractMetaFunction* func, - bool usePyArgs, TypeSystem::Language language, - const AbstractMetaArgument* lastArg); + /** + * Returns a map with all functions grouped, the function name is used as key. + * Example of return value: { "foo" -> ["foo(int)", "foo(int, long)], "bar" -> "bar(double)"} + * \param scope Where to search for functions, null means all global functions. + */ + QMap<QString, AbstractMetaFunctionList> getFunctionGroups(const AbstractMetaClass* scope = 0); + + /** + * Returns all different inherited overloads of func, and includes func as well. + * The function can be called multiple times without duplication. + * \param func the metafunction to be searched in subclasses. + * \param seen the function's minimal signatures already seen. + */ + AbstractMetaFunctionList getFunctionAndInheritedOverloads(const AbstractMetaFunction *func, QSet<QString> *seen); /// Write user's custom code snippets at class or module level. void writeCodeSnips(QTextStream& s, @@ -164,33 +122,9 @@ public: const AbstractMetaFunction* func, const AbstractMetaArgument* lastArg = 0); - /// Returns a string with the user's custom code snippets that comply with \p position and \p language. - QString getCodeSnippets(const QVector<CodeSnip> & codeSnips, TypeSystem::CodeSnipPosition position, TypeSystem::Language language); - /// Replaces variables for the user's custom code at global or class level. void processCodeSnip(QString& code, const AbstractMetaClass* context = 0); - /// Replaces the %CONVERTTOPYTHON type system variable. - inline void replaceConvertToPythonTypeSystemVariable(QString& code) - { - replaceConverterTypeSystemVariable(TypeSystemToPythonFunction, code); - } - /// Replaces the %CONVERTTOCPP type system variable. - inline void replaceConvertToCppTypeSystemVariable(QString& code) - { - replaceConverterTypeSystemVariable(TypeSystemToCppFunction, code); - } - /// Replaces the %ISCONVERTIBLE type system variable. - inline void replaceIsConvertibleToCppTypeSystemVariable(QString& code) - { - replaceConverterTypeSystemVariable(TypeSystemIsConvertibleFunction, code); - } - /// Replaces the %CHECKTYPE type system variable. - inline void replaceTypeCheckTypeSystemVariable(QString& code) - { - replaceConverterTypeSystemVariable(TypeSystemCheckFunction, code); - } - /** * Verifies if any of the function's code injections of the "native" * type needs the type system variable "%PYSELF". @@ -239,8 +173,8 @@ public: * \param arg_count the number of function arguments */ QString functionSignature(const AbstractMetaFunction* func, - QString prepend = QString(), - QString append = QString(), + const QString &prepend = QString(), + const QString &append = QString(), Options options = NoOption, int arg_count = -1) const; @@ -259,9 +193,6 @@ public: /// Returns a list of parent classes for a given class. AbstractMetaClassList getBaseClasses(const AbstractMetaClass* metaClass) const; - /// Returns a list of all ancestor classes for the given class. - AbstractMetaClassList getAllAncestors(const AbstractMetaClass* metaClass) const; - const AbstractMetaClass* getMultipleInheritingClass(const AbstractMetaClass* metaClass); void writeToPythonConversion(QTextStream& s, const AbstractMetaType* type, @@ -283,7 +214,8 @@ public: QString wrapperName(const AbstractMetaClass* metaClass) const; QString wrapperName(const AbstractMetaType *metaType) const; - QString fullPythonFunctionName(const AbstractMetaFunction* func); + QString fullPythonClassName(const AbstractMetaClass *metaClass); + QString fullPythonFunctionName(const AbstractMetaFunction *func); //WS static QString protectedEnumSurrogateName(const AbstractMetaEnum* metaEnum); static QString protectedFieldGetterName(const AbstractMetaField* field); @@ -292,16 +224,16 @@ public: static QString pythonPrimitiveTypeName(const QString& cppTypeName); static QString pythonPrimitiveTypeName(const PrimitiveTypeEntry* type); - static QString pythonOperatorFunctionName(QString cppOpFuncName); + static QString pythonOperatorFunctionName(const QString &cppOpFuncName); static QString pythonOperatorFunctionName(const AbstractMetaFunction* func); - static QString pythonRichCompareOperatorId(QString cppOpFuncName); + static QString pythonRichCompareOperatorId(const QString &cppOpFuncName); static QString pythonRichCompareOperatorId(const AbstractMetaFunction* func); static QString fixedCppTypeName(const CustomConversion::TargetToNativeConversion* toNative); static QString fixedCppTypeName(const AbstractMetaType* type); static QString fixedCppTypeName(const TypeEntry* type, QString typeName = QString()); - static bool isNumber(QString cpythonApiName); + static bool isNumber(const QString &cpythonApiName); static bool isNumber(const TypeEntry* type); static bool isNumber(const AbstractMetaType* type); static bool isPyInt(const TypeEntry* type); @@ -389,9 +321,10 @@ public: QString cpythonSetattroFunctionName(const AbstractMetaClass* metaClass); QString cpythonGetterFunctionName(const AbstractMetaField* metaField); QString cpythonSetterFunctionName(const AbstractMetaField* metaField); - QString cpythonWrapperCPtr(const AbstractMetaClass* metaClass, QString argName = QLatin1String(PYTHON_SELF_VAR)); - QString cpythonWrapperCPtr(const AbstractMetaType *metaType, QString argName); - QString cpythonWrapperCPtr(const TypeEntry* type, QString argName); + QString cpythonWrapperCPtr(const AbstractMetaClass* metaClass, + const QString &argName = QLatin1String("self")); + QString cpythonWrapperCPtr(const AbstractMetaType *metaType, const QString &argName); + QString cpythonWrapperCPtr(const TypeEntry* type, const QString &argName); /// Guesses the scope to where belongs an argument's default value. QString guessScopeForDefaultValue(const AbstractMetaFunction *func, @@ -414,6 +347,7 @@ public: QString getModuleHeaderFileName(const QString& moduleName = QString()) const; OptionDescriptions options() const override; + bool handleOption(const QString &key, const QString &value) override; /// Returns true if the user enabled the so called "parent constructor heuristic". bool useCtorHeuristic() const; @@ -426,6 +360,7 @@ public: /// Returns true if the generated code should use the "#define protected public" hack. bool avoidProtectedHack() const; QString cppApiVariableName(const QString& moduleName = QString()) const; + QString pythonModuleObjectName(const QString& moduleName = QString()) const; QString convertersVariableName(const QString& moduleName = QString()) const; /** * Returns the type index variable name for a given class. If \p alternativeTemplateName is true @@ -457,26 +392,12 @@ public: void writeMinimalConstructorExpression(QTextStream& s, const AbstractMetaType* type, const QString& defaultCtor = QString()); void writeMinimalConstructorExpression(QTextStream& s, const TypeEntry* type, const QString& defaultCtor = QString()); - /** - * Helper function to find for argument default value - */ - static QString getDefaultValue(const AbstractMetaFunction* func, const AbstractMetaArgument* arg); -protected: - bool doSetup(const QMap<QString, QString>& args); void collectContainerTypesFromConverterMacros(const QString& code, bool toPythonMacro); // verify whether the class is copyable bool isCopyable(const AbstractMetaClass* metaClass); - bool m_native_jump_table; - static QHash<QString, QString> m_pythonPrimitiveTypeName; - static QHash<QString, QString> m_pythonOperators; - static QHash<QString, QString> m_formatUnits; - static QHash<QString, QString> m_tpFuncs; - static QStringList m_knownPythonTypes; - void clearTpFuncs(); - const char* name() const { return "Shiboken"; } /// Initializes correspondences between primitive and Python types. static void initPrimitiveTypesCorrespondences(); @@ -505,6 +426,74 @@ protected: Indentor INDENT; + const QRegularExpression &convertToCppRegEx() const + { return m_typeSystemConvRegEx[TypeSystemToCppFunction]; } + + static QString pythonArgsAt(int i); + + static QHash<QString, QString> m_pythonPrimitiveTypeName; + static QHash<QString, QString> m_pythonOperators; + static QHash<QString, QString> m_formatUnits; + static QHash<QString, QString> m_tpFuncs; + static QStringList m_knownPythonTypes; + +private: + QString translateTypeForWrapperMethod(const AbstractMetaType* cType, + const AbstractMetaClass* context, + Options opt = NoOption) const; + + /** + * Returns all different inherited overloads of func. + * The function can be called multiple times without duplication. + * \param func the metafunction to be searched in subclasses. + * \param seen the function's minimal signatures already seen. + */ + AbstractMetaFunctionList getInheritedOverloads(const AbstractMetaFunction *func, + QSet<QString> *seen); + + /** + * Returns all overloads for a function named \p functionName. + * \param scope scope used to search for overloads. + * \param functionName the function name. + */ + AbstractMetaFunctionList getFunctionOverloads(const AbstractMetaClass* scope, + const QString& functionName); + /** + * Write a function argument in the C++ in the text stream \p s. + * This function just call \code s << argumentString(); \endcode + * \param s text stream used to write the output. + * \param func the current metafunction. + * \param argument metaargument information to be parsed. + * \param options some extra options. + */ + void writeArgument(QTextStream &s, + const AbstractMetaFunction* func, + const AbstractMetaArgument* argument, + Options options = NoOption) const; + /** + * Create a QString in the C++ format to an function argument. + * \param func the current metafunction. + * \param argument metaargument information to be parsed. + * \param options some extra options. + */ + QString argumentString(const AbstractMetaFunction* func, + const AbstractMetaArgument* argument, + Options options = NoOption) const; + + QString functionReturnType(const AbstractMetaFunction* func, Options options = NoOption) const; + + /// Utility function for writeCodeSnips. + typedef QPair<const AbstractMetaArgument*, QString> ArgumentVarReplacementPair; + typedef QVector<ArgumentVarReplacementPair> ArgumentVarReplacementList; + ArgumentVarReplacementList getArgumentReplacement(const AbstractMetaFunction* func, + bool usePyArgs, TypeSystem::Language language, + const AbstractMetaArgument* lastArg); + + /// Returns a string with the user's custom code snippets that comply with \p position and \p language. + QString getCodeSnippets(const QVector<CodeSnip> & codeSnips, + TypeSystem::CodeSnipPosition position, + TypeSystem::Language language); + enum TypeSystemConverterVariable { TypeSystemCheckFunction = 0, TypeSystemIsConvertibleFunction, @@ -514,15 +503,36 @@ protected: }; void replaceConverterTypeSystemVariable(TypeSystemConverterVariable converterVariable, QString& code); - static QString msgCouldNotFindMinimalConstructor(const QString &where, const QString &type); + /// Replaces the %CONVERTTOPYTHON type system variable. + inline void replaceConvertToPythonTypeSystemVariable(QString& code) + { + replaceConverterTypeSystemVariable(TypeSystemToPythonFunction, code); + } + /// Replaces the %CONVERTTOCPP type system variable. + inline void replaceConvertToCppTypeSystemVariable(QString& code) + { + replaceConverterTypeSystemVariable(TypeSystemToCppFunction, code); + } + /// Replaces the %ISCONVERTIBLE type system variable. + inline void replaceIsConvertibleToCppTypeSystemVariable(QString& code) + { + replaceConverterTypeSystemVariable(TypeSystemIsConvertibleFunction, code); + } + /// Replaces the %CHECKTYPE type system variable. + inline void replaceTypeCheckTypeSystemVariable(QString& code) + { + replaceConverterTypeSystemVariable(TypeSystemCheckFunction, code); + } -private: - bool m_useCtorHeuristic; - bool m_userReturnValueHeuristic; - bool m_usePySideExtensions; - bool m_verboseErrorMessagesDisabled; - bool m_useIsNullAsNbNonZero; - bool m_avoidProtectedHack; + /// Return a prefix with '_' suitable for names in C++ + QString moduleCppPrefix(const QString& moduleName = QString()) const; + + bool m_useCtorHeuristic = false; + bool m_userReturnValueHeuristic = false; + bool m_usePySideExtensions = false; + bool m_verboseErrorMessagesDisabled = false; + bool m_useIsNullAsNbNonZero = false; + bool m_avoidProtectedHack = false; typedef QHash<QString, AbstractMetaType*> AbstractMetaTypeCache; AbstractMetaTypeCache m_metaTypeFromStringCache; diff --git a/sources/shiboken2/libshiboken/autodecref.h b/sources/shiboken2/libshiboken/autodecref.h index 7b6aa47da..72fd236de 100644 --- a/sources/shiboken2/libshiboken/autodecref.h +++ b/sources/shiboken2/libshiboken/autodecref.h @@ -43,11 +43,6 @@ #include "sbkpython.h" #include "basewrapper.h" -#ifdef _MSC_VER -__pragma(warning(push)) -__pragma(warning(disable:4522)) // warning: C4522: 'Shiboken::AutoDecRef': multiple assignment operators specified -#endif - struct SbkObject; namespace Shiboken { @@ -58,6 +53,11 @@ namespace Shiboken struct LIBSHIBOKEN_API AutoDecRef { public: + AutoDecRef(const AutoDecRef&) = delete; + AutoDecRef(AutoDecRef&&) = delete; + AutoDecRef& operator=(const AutoDecRef&) = delete; + AutoDecRef& operator=(AutoDecRef&&) = delete; + /** * AutoDecRef constructor. * \param pyobj A borrowed reference to a Python object @@ -92,35 +92,18 @@ public: } /** - * Decref the current borrowed python reference and take the reference - * borrowed by \p other, so other.isNull() will return true. - */ - void operator=(AutoDecRef& other) - { - Py_XDECREF(m_pyObj); - m_pyObj = other.m_pyObj; - other.m_pyObj = 0; - } - - /** * Decref the current borrowed python reference and borrow \p other. */ - void operator=(PyObject* other) + void reset(PyObject* other) { Py_XDECREF(m_pyObj); m_pyObj = other; } private: PyObject* m_pyObj; - AutoDecRef(const AutoDecRef&); - AutoDecRef& operator=(const AutoDecRef&); }; } // namespace Shiboken -#ifdef _MSC_VER -__pragma(warning(pop)) -#endif - #endif // AUTODECREF_H diff --git a/sources/shiboken2/libshiboken/basewrapper.cpp b/sources/shiboken2/libshiboken/basewrapper.cpp index 5ca4172c8..c9e3b9d1b 100644 --- a/sources/shiboken2/libshiboken/basewrapper.cpp +++ b/sources/shiboken2/libshiboken/basewrapper.cpp @@ -40,6 +40,7 @@ #include "basewrapper.h" #include "basewrapper_p.h" #include "bindingmanager.h" +#include "helper.h" #include "sbkconverter.h" #include "sbkenum.h" #include "sbkstring.h" @@ -60,6 +61,15 @@ namespace { void _destroyParentInfo(SbkObject* obj, bool keepReference); } +static void callDestructor(const Shiboken::DtorAccumulatorVisitor::DestructorEntries &dts) +{ + for (const auto &e : dts) { + Shiboken::ThreadStateSaver threadSaver; + threadSaver.save(); + e.destructor(e.cppInstance); + } +} + extern "C" { @@ -118,20 +128,15 @@ static int SbkObject_traverse(PyObject* self, visitproc visit, void* arg) //Visit children Shiboken::ParentInfo* pInfo = sbkSelf->d->parentInfo; if (pInfo) { - std::set<SbkObject*>::const_iterator it = pInfo->children.begin(); - for(; it != pInfo->children.end(); ++it) - Py_VISIT(*it); + for (SbkObject *c : pInfo->children) + Py_VISIT(c); } //Visit refs Shiboken::RefCountMap* rInfo = sbkSelf->d->referredObjects; if (rInfo) { - Shiboken::RefCountMap::const_iterator it = rInfo->begin(); - for (; it != rInfo->end(); ++it) { - std::list<PyObject*>::const_iterator ref = it->second.begin(); - for(; ref != it->second.end(); ++ref) - Py_VISIT(*ref); - } + for (auto it = rInfo->begin(), end = rInfo->end(); it != end; ++it) + Py_VISIT(it->second); } if (sbkSelf->ob_dict) @@ -186,6 +191,12 @@ SbkObjectType *SbkObject_TypeF(void) return reinterpret_cast<SbkObjectType *>(type); } +static int mainThreadDeletionHandler(void *) +{ + if (Py_IsInitialized()) + Shiboken::BindingManager::instance().runDeletionInMainThread(); + return 0; +} static void SbkDeallocWrapperCommon(PyObject* pyObj, bool canDelete) { @@ -214,11 +225,32 @@ static void SbkDeallocWrapperCommon(PyObject* pyObj, bool canDelete) PyObject_ClearWeakRefs(pyObj); // If I have ownership and is valid delete C++ pointer - if (canDelete && sbkObj->d->hasOwnership && sbkObj->d->validCppObject) { - SbkObjectTypePrivate *sotp = PepType_SOTP(pyType); + SbkObjectTypePrivate *sotp{nullptr}; + canDelete &= sbkObj->d->hasOwnership && sbkObj->d->validCppObject; + if (canDelete) { + sotp = PepType_SOTP(pyType); + if (sotp->delete_in_main_thread && Shiboken::currentThreadId() != Shiboken::mainThreadId()) { + auto &bindingManager = Shiboken::BindingManager::instance(); + if (sotp->is_multicpp) { + Shiboken::DtorAccumulatorVisitor visitor(sbkObj); + Shiboken::walkThroughClassHierarchy(Py_TYPE(pyObj), &visitor); + for (const auto &e : visitor.entries()) + bindingManager.addToDeletionInMainThread(e); + } else { + Shiboken::DestructorEntry e{sotp->cpp_dtor, sbkObj->d->cptr[0]}; + bindingManager.addToDeletionInMainThread(e); + } + Py_AddPendingCall(mainThreadDeletionHandler, nullptr); + canDelete = false; + } + } + + if (canDelete) { if (sotp->is_multicpp) { - Shiboken::DeallocVisitor visitor(sbkObj); + Shiboken::DtorAccumulatorVisitor visitor(sbkObj); Shiboken::walkThroughClassHierarchy(Py_TYPE(pyObj), &visitor); + Shiboken::Object::deallocData(sbkObj, true); + callDestructor(visitor.entries()); } else { void* cptr = sbkObj->d->cptr[0]; Shiboken::Object::deallocData(sbkObj, true); @@ -325,7 +357,7 @@ PyObject* SbkObjectTypeTpNew(PyTypeObject* metatype, PyObject* args, PyObject* k Shiboken::ObjectType::initPrivateData(newType); SbkObjectTypePrivate *sotp = PepType_SOTP(newType); - std::list<SbkObjectType*> bases = Shiboken::getCppBaseClasses(reinterpret_cast<PyTypeObject*>(newType)); + const auto bases = Shiboken::getCppBaseClasses(reinterpret_cast<PyTypeObject*>(newType)); if (bases.size() == 1) { SbkObjectTypePrivate *parentType = PepType_SOTP(bases.front()); sotp->mi_offsets = parentType->mi_offsets; @@ -352,10 +384,9 @@ PyObject* SbkObjectTypeTpNew(PyTypeObject* metatype, PyObject* args, PyObject* k sotp->d_func = nullptr; sotp->is_user_type = 1; - std::list<SbkObjectType*>::const_iterator it = bases.begin(); - for (; it != bases.end(); ++it) { - if (PepType_SOTP(*it)->subtype_init) - PepType_SOTP(*it)->subtype_init(newType, args, kwds); + for (SbkObjectType *base : bases) { + if (PepType_SOTP(base)->subtype_init) + PepType_SOTP(base)->subtype_init(newType, args, kwds); } return reinterpret_cast<PyObject*>(newType); @@ -455,37 +486,22 @@ void _destroyParentInfo(SbkObject* obj, bool keepReference) namespace Shiboken { - -static void decRefPyObjectList(const std::list<PyObject*> &pyObj, PyObject* skip = 0); - -static void _walkThroughClassHierarchy(PyTypeObject* currentType, HierarchyVisitor* visitor) +bool walkThroughClassHierarchy(PyTypeObject *currentType, HierarchyVisitor *visitor) { PyObject* bases = currentType->tp_bases; Py_ssize_t numBases = PyTuple_GET_SIZE(bases); - for (int i = 0; i < numBases; ++i) { + bool result = false; + for (int i = 0; !result && i < numBases; ++i) { PyTypeObject* type = reinterpret_cast<PyTypeObject*>(PyTuple_GET_ITEM(bases, i)); - - if (!PyType_IsSubtype(type, reinterpret_cast<PyTypeObject*>(SbkObject_TypeF()))) { - continue; - } else { + if (PyType_IsSubtype(type, reinterpret_cast<PyTypeObject*>(SbkObject_TypeF()))) { SbkObjectType* sbkType = reinterpret_cast<SbkObjectType*>(type); - if (PepType_SOTP(sbkType)->is_user_type) - _walkThroughClassHierarchy(type, visitor); - else - visitor->visit(sbkType); + result = PepType_SOTP(sbkType)->is_user_type + ? walkThroughClassHierarchy(type, visitor) : visitor->visit(sbkType); } - if (visitor->wasFinished()) - break; } + return result; } -void walkThroughClassHierarchy(PyTypeObject* currentType, HierarchyVisitor* visitor) -{ - _walkThroughClassHierarchy(currentType, visitor); - visitor->done(); -} - - bool importModule(const char* moduleName, PyTypeObject*** cppApiPtr) { PyObject* sysModules = PyImport_GetModuleDict(); @@ -517,27 +533,36 @@ bool importModule(const char* moduleName, PyTypeObject*** cppApiPtr) // Wrapper metatype and base type ---------------------------------------------------------- -void DtorCallerVisitor::visit(SbkObjectType* node) +HierarchyVisitor::HierarchyVisitor() = default; +HierarchyVisitor::~HierarchyVisitor() = default; + +bool BaseCountVisitor::visit(SbkObjectType *) { - m_ptrs.push_back(std::make_pair(m_pyObj->d->cptr[m_ptrs.size()], node)); + m_count++; + return false; } -void DtorCallerVisitor::done() +bool BaseAccumulatorVisitor::visit(SbkObjectType *node) { - std::list<std::pair<void*, SbkObjectType*> >::const_iterator it = m_ptrs.begin(); - for (; it != m_ptrs.end(); ++it) { - Shiboken::ThreadStateSaver threadSaver; - threadSaver.save(); - PepType_SOTP(it->second)->cpp_dtor(it->first); - } + m_bases.push_back(node); + return false; } -void DeallocVisitor::done() +bool GetIndexVisitor::visit(SbkObjectType *node) { - Shiboken::Object::deallocData(m_pyObj, true); - DtorCallerVisitor::done(); + m_index++; + return PyType_IsSubtype(reinterpret_cast<PyTypeObject*>(node), m_desiredType); } +bool DtorAccumulatorVisitor::visit(SbkObjectType *node) +{ + m_entries.push_back(DestructorEntry{PepType_SOTP(node)->cpp_dtor, + m_pyObject->d->cptr[m_entries.size()]}); + return false; +} + +void _initMainThreadId(); // helper.cpp + namespace Conversions { void init(); } void init() @@ -546,6 +571,8 @@ void init() if (shibokenAlreadInitialised) return; + _initMainThreadId(); + Conversions::init(); PyEval_InitThreads(); @@ -609,25 +636,21 @@ void setErrorAboutWrongArguments(PyObject* args, const char* funcName, const cha class FindBaseTypeVisitor : public HierarchyVisitor { - public: - FindBaseTypeVisitor(PyTypeObject* typeToFind) : m_found(false), m_typeToFind(typeToFind) {} - virtual void visit(SbkObjectType* node) - { - if (reinterpret_cast<PyTypeObject*>(node) == m_typeToFind) { - m_found = true; - finish(); - } - } - bool found() const { return m_found; } +public: + explicit FindBaseTypeVisitor(PyTypeObject *typeToFind) : m_typeToFind(typeToFind) {} + + bool visit(SbkObjectType *node) override + { + return reinterpret_cast<PyTypeObject*>(node) == m_typeToFind; + } - private: - bool m_found; - PyTypeObject* m_typeToFind; +private: + PyTypeObject *m_typeToFind; }; -std::list<SbkObject*> splitPyObject(PyObject* pyObj) +std::vector<SbkObject *> splitPyObject(PyObject* pyObj) { - std::list<SbkObject*> result; + std::vector<SbkObject *> result; if (PySequence_Check(pyObj)) { AutoDecRef lst(PySequence_Fast(pyObj, "Invalid keep reference object.")); if (!lst.isNull()) { @@ -643,14 +666,11 @@ std::list<SbkObject*> splitPyObject(PyObject* pyObj) return result; } -static void decRefPyObjectList(const std::list<PyObject*>& lst, PyObject *skip) +template <class Iterator> +inline void decRefPyObjectList(Iterator i1, Iterator i2) { - std::list<PyObject*>::const_iterator iter = lst.begin(); - while(iter != lst.end()) { - if (*iter != skip) - Py_DECREF(*iter); - ++iter; - } + for (; i1 != i2; ++i1) + Py_DECREF(i1->second); } namespace ObjectType @@ -669,8 +689,7 @@ bool isUserType(PyTypeObject* type) bool canCallConstructor(PyTypeObject* myType, PyTypeObject* ctorType) { FindBaseTypeVisitor visitor(ctorType); - walkThroughClassHierarchy(myType, &visitor); - if (!visitor.found()) { + if (!walkThroughClassHierarchy(myType, &visitor)) { PyErr_Format(PyExc_TypeError, "%s isn't a direct base class of %s", ctorType->tp_name, myType->tp_name); return false; } @@ -748,7 +767,7 @@ introduceWrapperType(PyObject *enclosingObject, ObjectDestructor cppObjDtor, SbkObjectType *baseType, PyObject *baseTypes, - bool isInnerClass) + unsigned wrapperFlags) { if (baseType) { typeSpec->slots[0].pfunc = reinterpret_cast<void *>(baseType); @@ -773,10 +792,14 @@ introduceWrapperType(PyObject *enclosingObject, return nullptr; initPrivateData(type); + auto sotp = PepType_SOTP(type); + if (wrapperFlags & DeleteInMainThread) + sotp->delete_in_main_thread = 1; + setOriginalName(type, originalName); setDestructorFunction(type, cppObjDtor); - if (isInnerClass) { + if (wrapperFlags & InnerClass) { if (PyDict_SetItemString(enclosingObject, typeName, reinterpret_cast<PyObject *>(type)) == 0) return type; else @@ -844,12 +867,13 @@ static void setSequenceOwnership(PyObject* pyObj, bool owner) if (PySequence_Check(pyObj) && has_length) { Py_ssize_t size = PySequence_Size(pyObj); if (size > 0) { - std::list<SbkObject*> objs = splitPyObject(pyObj); - for (auto it = objs.begin(), end = objs.end(); it != end; ++it) { - if (owner) - getOwnership(*it); - else - releaseOwnership(*it); + const auto objs = splitPyObject(pyObj); + if (owner) { + for (SbkObject *o : objs) + getOwnership(o); + } else { + for (SbkObject *o : objs) + releaseOwnership(o); } } } else if (Object::checkType(pyObj)) { @@ -885,8 +909,9 @@ void callCppDestructors(SbkObject* pyObj) PyTypeObject *type = Py_TYPE(pyObj); SbkObjectTypePrivate * sotp = PepType_SOTP(type); if (sotp->is_multicpp) { - Shiboken::DtorCallerVisitor visitor(pyObj); + Shiboken::DtorAccumulatorVisitor visitor(pyObj); Shiboken::walkThroughClassHierarchy(type, &visitor); + callDestructor(visitor.entries()); } else { Shiboken::ThreadStateSaver threadSaver; threadSaver.save(); @@ -977,10 +1002,9 @@ void invalidate(SbkObject* self) static void recursive_invalidate(PyObject* pyobj, std::set<SbkObject*>& seen) { - std::list<SbkObject*> objs = splitPyObject(pyobj); - std::list<SbkObject*>::const_iterator it = objs.begin(); - for (; it != objs.end(); it++) - recursive_invalidate(*it, seen); + const auto objs = splitPyObject(pyobj); + for (SbkObject *o : objs) + recursive_invalidate(o, seen); } static void recursive_invalidate(SbkObject* self, std::set<SbkObject*>& seen) @@ -999,30 +1023,22 @@ static void recursive_invalidate(SbkObject* self, std::set<SbkObject*>& seen) if (self->d->parentInfo) { // Create a copy because this list can be changed during the process ChildrenList copy = self->d->parentInfo->children; - ChildrenList::iterator it = copy.begin(); - for (; it != copy.end(); ++it) { + for (SbkObject *child : copy) { // invalidate the child - recursive_invalidate(*it, seen); + recursive_invalidate(child, seen); // if the parent not is a wrapper class, then remove children from him, because We do not know when this object will be destroyed if (!self->d->validCppObject) - removeParent(*it, true, true); + removeParent(child, true, true); } } // If has ref to other objects invalidate all if (self->d->referredObjects) { RefCountMap& refCountMap = *(self->d->referredObjects); - RefCountMap::iterator iter; - for (iter = refCountMap.begin(); iter != refCountMap.end(); ++iter) { - const std::list<PyObject*> lst = iter->second; - std::list<PyObject*>::const_iterator it = lst.begin(); - while(it != lst.end()) { - recursive_invalidate(*it, seen); - ++it; - } - } + for (auto it = refCountMap.begin(), end = refCountMap.end(); it != end; ++it) + recursive_invalidate(it->second, seen); } } @@ -1037,23 +1053,17 @@ void makeValid(SbkObject* self) // If it is a parent make all children valid if (self->d->parentInfo) { - ChildrenList::iterator it = self->d->parentInfo->children.begin(); - for (; it != self->d->parentInfo->children.end(); ++it) - makeValid(*it); + for (SbkObject *child : self->d->parentInfo->children) + makeValid(child); } // If has ref to other objects make all valid again if (self->d->referredObjects) { RefCountMap& refCountMap = *(self->d->referredObjects); RefCountMap::iterator iter; - for (iter = refCountMap.begin(); iter != refCountMap.end(); ++iter) { - const std::list<PyObject*> lst = iter->second; - std::list<PyObject*>::const_iterator it = lst.begin(); - while(it != lst.end()) { - if (Shiboken::Object::checkType(*it)) - makeValid(reinterpret_cast<SbkObject*>(*it)); - ++it; - } + for (auto it = refCountMap.begin(), end = refCountMap.end(); it != end; ++it) { + if (Shiboken::Object::checkType(it->second)) + makeValid(reinterpret_cast<SbkObject *>(it->second)); } } } @@ -1168,15 +1178,14 @@ SbkObject *findColocatedChild(SbkObject *wrapper, ChildrenList& children = pInfo->children; - ChildrenList::iterator childrenEnd = children.end(); - for (ChildrenList::iterator iChild = children.begin(); iChild != childrenEnd; ++iChild) { - if (!((*iChild)->d && (*iChild)->d->cptr)) + for (SbkObject *child : children) { + if (!(child->d && child->d->cptr)) continue; - if ((*iChild)->d->cptr[0] == wrapper->d->cptr[0]) { - if (reinterpret_cast<const void *>(Py_TYPE(*iChild)) == reinterpret_cast<const void *>(instanceType)) - return const_cast<SbkObject *>((*iChild)); + if (child->d->cptr[0] == wrapper->d->cptr[0]) { + if (reinterpret_cast<const void *>(Py_TYPE(child)) == reinterpret_cast<const void *>(instanceType)) + return child; else - return findColocatedChild(const_cast<SbkObject *>(*iChild), instanceType); + return findColocatedChild(child, instanceType); } } return 0; @@ -1435,57 +1444,56 @@ void* getTypeUserData(SbkObject* wrapper) return PepType_SOTP(Py_TYPE(wrapper))->user_data; } -void keepReference(SbkObject* self, const char* key, PyObject* referredObject, bool append) +static inline bool isNone(const PyObject *o) { - bool isNone = (!referredObject || (referredObject == Py_None)); - - if (!self->d->referredObjects) - self->d->referredObjects = new Shiboken::RefCountMap; - - RefCountMap& refCountMap = *(self->d->referredObjects); - RefCountMap::iterator iter = refCountMap.find(key); - std::list<PyObject*> objects; - if (iter != refCountMap.end()) { - objects = (*iter).second; - std::list<PyObject*>::const_iterator found = std::find(objects.begin(), objects.end(), referredObject); - - // skip if objects already exists - if (found != objects.end()) - return; - } + return o == nullptr || o == Py_None; +} - if (append && !isNone) { - refCountMap[key].push_back(referredObject); - Py_INCREF(referredObject); - } else if (!append) { - if (objects.size() > 0) - decRefPyObjectList(objects, isNone ? 0 : referredObject); - if (isNone) { - if (iter != refCountMap.end()) - refCountMap.erase(iter); - } else { - objects.clear(); - objects.push_back(referredObject); - refCountMap[key] = objects; - Py_INCREF(referredObject); +static void removeRefCountKey(SbkObject* self, const char *key) +{ + if (self->d->referredObjects) { + const auto iterPair = self->d->referredObjects->equal_range(key); + if (iterPair.first != iterPair.second) { + decRefPyObjectList(iterPair.first, iterPair.second); + self->d->referredObjects->erase(iterPair.first, iterPair.second); } } } -void removeReference(SbkObject* self, const char* key, PyObject* referredObject) +void keepReference(SbkObject* self, const char* key, PyObject* referredObject, bool append) { - if (!referredObject || (referredObject == Py_None)) + if (isNone(referredObject)) { + removeRefCountKey(self, key); return; + } - if (!self->d->referredObjects) + if (!self->d->referredObjects) { + self->d->referredObjects = + new Shiboken::RefCountMap{RefCountMap::value_type{key, referredObject}}; + Py_INCREF(referredObject); return; + } RefCountMap& refCountMap = *(self->d->referredObjects); - RefCountMap::iterator iter = refCountMap.find(key); - if (iter != refCountMap.end()) { - decRefPyObjectList(iter->second); - refCountMap.erase(iter); + const auto iterPair = refCountMap.equal_range(key); + if (std::any_of(iterPair.first, iterPair.second, + [referredObject](const RefCountMap::value_type &v) { return v.second == referredObject; })) { + return; + } + + if (!append && iterPair.first != iterPair.second) { + decRefPyObjectList(iterPair.first, iterPair.second); + refCountMap.erase(iterPair.first, iterPair.second); } + + refCountMap.insert(RefCountMap::value_type{key, referredObject}); + Py_INCREF(referredObject); +} + +void removeReference(SbkObject* self, const char* key, PyObject* referredObject) +{ + if (!isNone(referredObject)) + removeRefCountKey(self, key); } void clearReferences(SbkObject* self) @@ -1494,27 +1502,27 @@ void clearReferences(SbkObject* self) return; RefCountMap& refCountMap = *(self->d->referredObjects); - RefCountMap::iterator iter; - for (iter = refCountMap.begin(); iter != refCountMap.end(); ++iter) - decRefPyObjectList(iter->second); + for (auto it = refCountMap.begin(), end = refCountMap.end(); it != end; ++it) + Py_DECREF(it->second); self->d->referredObjects->clear(); } std::string info(SbkObject* self) { std::ostringstream s; - std::list<SbkObjectType*> bases; if (self->d && self->d->cptr) { + std::vector<SbkObjectType *> bases; if (ObjectType::isUserType(Py_TYPE(self))) bases = getCppBaseClasses(Py_TYPE(self)); else bases.push_back(reinterpret_cast<SbkObjectType*>(Py_TYPE(self))); s << "C++ address....... "; - std::list<SbkObjectType*>::const_iterator it = bases.begin(); - for (int i = 0; it != bases.end(); ++it, ++i) - s << reinterpret_cast<PyTypeObject *>(*it)->tp_name << '/' << self->d->cptr[i] << ' '; + for (size_t i = 0, size = bases.size(); i < size; ++i) { + auto base = reinterpret_cast<PyTypeObject *>(bases[i]); + s << base->tp_name << '/' << self->d->cptr[i] << ' '; + } s << "\n"; } else { @@ -1535,9 +1543,8 @@ std::string info(SbkObject* self) if (self->d->parentInfo && !self->d->parentInfo->children.empty()) { s << "children.......... "; - ChildrenList& children = self->d->parentInfo->children; - for (ChildrenList::const_iterator it = children.begin(); it != children.end(); ++it) { - Shiboken::AutoDecRef child(PyObject_Str(reinterpret_cast<PyObject *>(*it))); + for (SbkObject *sbkChild : self->d->parentInfo->children) { + Shiboken::AutoDecRef child(PyObject_Str(reinterpret_cast<PyObject *>(sbkChild))); s << String::toCString(child) << ' '; } s << '\n'; @@ -1546,17 +1553,16 @@ std::string info(SbkObject* self) if (self->d->referredObjects && !self->d->referredObjects->empty()) { Shiboken::RefCountMap& map = *self->d->referredObjects; s << "referred objects.. "; - Shiboken::RefCountMap::const_iterator it = map.begin(); - for (; it != map.end(); ++it) { - if (it != map.begin()) - s << " "; - s << '"' << it->first << "\" => "; - std::list<PyObject*>::const_iterator j = it->second.begin(); - for (; j != it->second.end(); ++j) { - Shiboken::AutoDecRef obj(PyObject_Str(*j)); - s << String::toCString(obj) << ' '; + std::string lastKey; + for (auto it = map.begin(), end = map.end(); it != end; ++it) { + if (it->first != lastKey) { + if (!lastKey.empty()) + s << " "; + s << '"' << it->first << "\" => "; + lastKey = it->first; } - s << ' '; + Shiboken::AutoDecRef obj(PyObject_Str(it->second)); + s << String::toCString(obj) << ' '; } s << '\n'; } diff --git a/sources/shiboken2/libshiboken/basewrapper.h b/sources/shiboken2/libshiboken/basewrapper.h index 134a3bc51..65849d783 100644 --- a/sources/shiboken2/libshiboken/basewrapper.h +++ b/sources/shiboken2/libshiboken/basewrapper.h @@ -190,6 +190,12 @@ LIBSHIBOKEN_API void setDestructorFunction(SbkObjectType* self, ObjectDes LIBSHIBOKEN_API void initPrivateData(SbkObjectType* self); +enum WrapperFlags +{ + InnerClass = 0x1, + DeleteInMainThread = 0x2 +}; + /** * Initializes a Shiboken wrapper type and adds it to the module, * or to the enclosing class if the type is an inner class. @@ -215,7 +221,7 @@ LIBSHIBOKEN_API SbkObjectType *introduceWrapperType(PyObject *enclosingObject, ObjectDestructor cppObjDtor, SbkObjectType *baseType, PyObject *baseTypes, - bool isInnerClass); + unsigned wrapperFlags = 0); /** * Set the subtype init hook for a type. diff --git a/sources/shiboken2/libshiboken/basewrapper_p.h b/sources/shiboken2/libshiboken/basewrapper_p.h index ebd2648e7..f8a381078 100644 --- a/sources/shiboken2/libshiboken/basewrapper_p.h +++ b/sources/shiboken2/libshiboken/basewrapper_p.h @@ -43,10 +43,10 @@ #include "sbkpython.h" #include "basewrapper.h" -#include <list> -#include <map> +#include <unordered_map> #include <set> #include <string> +#include <vector> struct SbkObject; struct SbkObjectType; @@ -58,7 +58,7 @@ namespace Shiboken * This mapping associates a method and argument of an wrapper object with the wrapper of * said argument when it needs the binding to help manage its reference count. */ -typedef std::map<std::string, std::list<PyObject*> > RefCountMap; +typedef std::unordered_multimap<std::string, PyObject *> RefCountMap; /// Linked list of SbkBaseWrapper pointers typedef std::set<SbkObject*> ChildrenList; @@ -137,6 +137,7 @@ struct SbkObjectTypePrivate /// Tells is the type is a value type or an object-type, see BEHAVIOUR_* constants. // TODO-CONVERTERS: to be deprecated/removed int type_behaviour : 2; + int delete_in_main_thread : 1; /// C++ name char* original_name; /// Type user data @@ -150,10 +151,21 @@ struct SbkObjectTypePrivate namespace Shiboken { + +/** + * \internal + * Data required to invoke a C++ destructor + */ +struct DestructorEntry +{ + ObjectDestructor destructor; + void *cppInstance; +}; + /** * Utility function used to transform a PyObject that implements sequence protocol into a std::list. **/ -std::list<SbkObject*> splitPyObject(PyObject* pyObj); +std::vector<SbkObject *> splitPyObject(PyObject* pyObj); /** * Visitor class used by walkOnClassHierarchy function. @@ -161,81 +173,71 @@ std::list<SbkObject*> splitPyObject(PyObject* pyObj); class HierarchyVisitor { public: - HierarchyVisitor() : m_wasFinished(false) {} - virtual ~HierarchyVisitor() {} - virtual void visit(SbkObjectType* node) = 0; - virtual void done() {} - void finish() { m_wasFinished = true; }; - bool wasFinished() const { return m_wasFinished; } -private: - bool m_wasFinished; + HierarchyVisitor(const HierarchyVisitor &) = delete; + HierarchyVisitor(HierarchyVisitor &&) = delete; + HierarchyVisitor &operator=(const HierarchyVisitor &) = delete; + HierarchyVisitor &operator=(HierarchyVisitor &&) = delete; + + HierarchyVisitor(); + virtual ~HierarchyVisitor(); + + virtual bool visit(SbkObjectType *node) = 0; // return true to terminate }; class BaseCountVisitor : public HierarchyVisitor { public: - BaseCountVisitor() : m_count(0) {} - - void visit(SbkObjectType*) - { - m_count++; - } + bool visit(SbkObjectType *) override; int count() const { return m_count; } + private: - int m_count; + int m_count = 0; }; class BaseAccumulatorVisitor : public HierarchyVisitor { public: - BaseAccumulatorVisitor() {} + typedef std::vector<SbkObjectType *> Result; - void visit(SbkObjectType* node) - { - m_bases.push_back(node); - } + bool visit(SbkObjectType *node) override; + + Result bases() const { return m_bases; } - std::list<SbkObjectType*> bases() const { return m_bases; } private: - std::list<SbkObjectType*> m_bases; + Result m_bases; }; class GetIndexVisitor : public HierarchyVisitor { public: - GetIndexVisitor(PyTypeObject* desiredType) : m_index(-1), m_desiredType(desiredType) {} - virtual void visit(SbkObjectType* node) - { - m_index++; - if (PyType_IsSubtype(reinterpret_cast<PyTypeObject*>(node), m_desiredType)) - finish(); - } + explicit GetIndexVisitor(PyTypeObject* desiredType) : m_desiredType(desiredType) {} + + bool visit(SbkObjectType *node) override; + int index() const { return m_index; } private: - int m_index; - PyTypeObject* m_desiredType; + int m_index = -1; + PyTypeObject *m_desiredType; }; -/// Call the destructor of each C++ object held by a Python object -class DtorCallerVisitor : public HierarchyVisitor +/// Collect destructors and C++ instances of each C++ object held by a Python +/// object +class DtorAccumulatorVisitor : public HierarchyVisitor { public: - DtorCallerVisitor(SbkObject* pyObj) : m_pyObj(pyObj) {} - void visit(SbkObjectType* node); - void done(); -protected: - std::list<std::pair<void*, SbkObjectType*> > m_ptrs; - SbkObject* m_pyObj; -}; + explicit DtorAccumulatorVisitor(SbkObject *pyObj) : m_pyObject(pyObj) {} -/// Dealloc of each C++ object held by a Python object, this implies a call to the C++ object destructor -class DeallocVisitor : public DtorCallerVisitor -{ -public: - DeallocVisitor(SbkObject* pyObj) : DtorCallerVisitor(pyObj) {} - void done(); + bool visit(SbkObjectType *node) override; + + using DestructorEntries = std::vector<DestructorEntry>; + + const DestructorEntries &entries() const { return m_entries; } + +private: + DestructorEntries m_entries; + SbkObject *m_pyObject; }; /// \internal Internal function used to walk on classes inheritance trees. @@ -244,7 +246,7 @@ public: * For each pure Shiboken type found, HiearchyVisitor::visit is called and the algorithm consider * all children of this type as visited. */ -void walkThroughClassHierarchy(PyTypeObject* currentType, HierarchyVisitor* visitor); +bool walkThroughClassHierarchy(PyTypeObject *currentType, HierarchyVisitor *visitor); inline int getTypeIndexOnHierarchy(PyTypeObject* baseType, PyTypeObject* desiredType) { @@ -260,7 +262,7 @@ inline int getNumberOfCppBaseClasses(PyTypeObject* baseType) return visitor.count(); } -inline std::list<SbkObjectType*> getCppBaseClasses(PyTypeObject* baseType) +inline std::vector<SbkObjectType *> getCppBaseClasses(PyTypeObject* baseType) { BaseAccumulatorVisitor visitor; walkThroughClassHierarchy(baseType, &visitor); diff --git a/sources/shiboken2/libshiboken/bindingmanager.cpp b/sources/shiboken2/libshiboken/bindingmanager.cpp index 0aa520b38..8a9e912fd 100644 --- a/sources/shiboken2/libshiboken/bindingmanager.cpp +++ b/sources/shiboken2/libshiboken/bindingmanager.cpp @@ -57,14 +57,12 @@ typedef std::unordered_map<const void *, SbkObject *> WrapperMap; class Graph { public: - typedef std::list<SbkObjectType*> NodeList; + typedef std::vector<SbkObjectType *> NodeList; typedef std::unordered_map<SbkObjectType *, NodeList> Edges; Edges m_edges; - Graph() - { - } + Graph() = default; void addEdge(SbkObjectType* from, SbkObjectType* to) { @@ -78,14 +76,13 @@ public: file << "digraph D {\n"; - Edges::const_iterator i = m_edges.begin(); - for (; i != m_edges.end(); ++i) { - SbkObjectType* node1 = i->first; + for (auto i = m_edges.begin(), end = m_edges.end(); i != end; ++i) { + auto node1 = reinterpret_cast<const PyTypeObject *>(i->first); const NodeList& nodeList = i->second; - NodeList::const_iterator j = nodeList.begin(); - for (; j != nodeList.end(); ++j) { - file << '"' << reinterpret_cast<PyTypeObject *>(*j)->tp_name << "\" -> \"" - << reinterpret_cast<PyTypeObject *>(node1)->tp_name << "\"\n"; + for (const SbkObjectType *o : nodeList) { + auto node2 = reinterpret_cast<const PyTypeObject *>(o); + file << '"' << node2->tp_name << "\" -> \"" + << node1->tp_name << "\"\n"; } } file << "}\n"; @@ -97,9 +94,8 @@ public: Edges::const_iterator edgesIt = m_edges.find(type); if (edgesIt != m_edges.end()) { const NodeList& adjNodes = m_edges.find(type)->second; - NodeList::const_iterator i = adjNodes.begin(); - for (; i != adjNodes.end(); ++i) { - SbkObjectType* newType = identifyType(cptr, *i, baseType); + for (SbkObjectType *node : adjNodes) { + SbkObjectType* newType = identifyType(cptr, node, baseType); if (newType) return newType; } @@ -115,9 +111,8 @@ public: if (typeFound != type) *cptr = typeFound; return type; - } else { - return nullptr; } + return nullptr; } }; @@ -128,10 +123,9 @@ static void showWrapperMap(const WrapperMap& wrapperMap) if (Py_VerboseFlag > 0) { fprintf(stderr, "-------------------------------\n"); fprintf(stderr, "WrapperMap: %p (size: %d)\n", &wrapperMap, (int) wrapperMap.size()); - WrapperMap::const_iterator iter; - for (iter = wrapperMap.begin(); iter != wrapperMap.end(); ++iter) { - const SbkObject *sbkObj = iter->second; - fprintf(stderr, "key: %p, value: %p (%s, refcnt: %d)\n", iter->first, + for (auto it = wrapperMap.begin(), end = wrapperMap.end(); it != end; ++it) { + const SbkObject *sbkObj = it->second; + fprintf(stderr, "key: %p, value: %p (%s, refcnt: %d)\n", it->first, static_cast<const void *>(sbkObj), (Py_TYPE(sbkObj))->tp_name, int(reinterpret_cast<const PyObject *>(sbkObj)->ob_refcnt)); @@ -142,8 +136,11 @@ static void showWrapperMap(const WrapperMap& wrapperMap) #endif struct BindingManager::BindingManagerPrivate { + using DestructorEntries = std::vector<DestructorEntry>; + WrapperMap wrapperMapper; Graph classHierarchy; + DestructorEntries deleteInMainThread; bool destroying; BindingManagerPrivate() : destroying(false) {} @@ -255,6 +252,18 @@ void BindingManager::releaseWrapper(SbkObject* sbkObj) sbkObj->d->validCppObject = false; } +void BindingManager::runDeletionInMainThread() +{ + for (const DestructorEntry &e : m_d->deleteInMainThread) + e.destructor(e.cppInstance); + m_d->deleteInMainThread.clear(); +} + +void BindingManager::addToDeletionInMainThread(const DestructorEntry &e) +{ + m_d->deleteInMainThread.push_back(e); +} + SbkObject* BindingManager::retrieveWrapper(const void* cptr) { WrapperMap::iterator iter = m_d->wrapperMapper.find(cptr); diff --git a/sources/shiboken2/libshiboken/bindingmanager.h b/sources/shiboken2/libshiboken/bindingmanager.h index 13a4d3a2e..d03aa999a 100644 --- a/sources/shiboken2/libshiboken/bindingmanager.h +++ b/sources/shiboken2/libshiboken/bindingmanager.h @@ -50,11 +50,18 @@ struct SbkObjectType; namespace Shiboken { +struct DestructorEntry; + typedef void (*ObjectVisitor)(SbkObject*, void*); class LIBSHIBOKEN_API BindingManager { public: + BindingManager(const BindingManager&) = delete; + BindingManager(BindingManager&&) = delete; + BindingManager& operator=(const BindingManager&) = delete; + BindingManager& operator=(BindingManager&&) = delete; + static BindingManager& instance(); bool hasWrapper(const void *cptr); @@ -62,6 +69,9 @@ public: void registerWrapper(SbkObject* pyObj, void* cptr); void releaseWrapper(SbkObject* wrapper); + void runDeletionInMainThread(); + void addToDeletionInMainThread(const DestructorEntry &); + SbkObject* retrieveWrapper(const void* cptr); PyObject* getOverride(const void* cptr, const char* methodName); @@ -94,10 +104,7 @@ public: private: ~BindingManager(); - // disable copy BindingManager(); - BindingManager(const BindingManager&); - BindingManager& operator=(const BindingManager&); struct BindingManagerPrivate; BindingManagerPrivate* m_d; diff --git a/sources/shiboken2/libshiboken/gilstate.h b/sources/shiboken2/libshiboken/gilstate.h index 00b049802..9da4871d1 100644 --- a/sources/shiboken2/libshiboken/gilstate.h +++ b/sources/shiboken2/libshiboken/gilstate.h @@ -49,6 +49,11 @@ namespace Shiboken class LIBSHIBOKEN_API GilState { public: + GilState(const GilState&) = delete; + GilState(GilState&&) = delete; + GilState& operator=(const GilState&) = delete; + GilState& operator=(GilState&&) = delete; + GilState(); ~GilState(); void release(); diff --git a/sources/shiboken2/libshiboken/helper.cpp b/sources/shiboken2/libshiboken/helper.cpp index 472924723..e42daff07 100644 --- a/sources/shiboken2/libshiboken/helper.cpp +++ b/sources/shiboken2/libshiboken/helper.cpp @@ -41,6 +41,12 @@ #include "sbkstring.h" #include <stdarg.h> +#ifdef _WIN32 +# include <windows.h> +#else +# include <pthread.h> +#endif + namespace Shiboken { @@ -141,4 +147,24 @@ int warning(PyObject* category, int stacklevel, const char* format, ...) return result; } +ThreadId currentThreadId() +{ +#if defined(_WIN32) + return GetCurrentThreadId(); +#elif defined(__APPLE_CC__) + return reinterpret_cast<ThreadId>(pthread_self()); +#else + return pthread_self(); +#endif +} + +// Internal, used by init() from main thread +static ThreadId _mainThreadId{0}; +void _initMainThreadId() { _mainThreadId = currentThreadId(); } + +ThreadId mainThreadId() +{ + return _mainThreadId; +} + } // namespace Shiboken diff --git a/sources/shiboken2/libshiboken/helper.h b/sources/shiboken2/libshiboken/helper.h index b215142c7..9b6d8903f 100644 --- a/sources/shiboken2/libshiboken/helper.h +++ b/sources/shiboken2/libshiboken/helper.h @@ -77,7 +77,12 @@ template<class T> class AutoArrayPointer { public: - AutoArrayPointer(int size) { data = new T[size]; } + AutoArrayPointer(const AutoArrayPointer&) = delete; + AutoArrayPointer(AutoArrayPointer&&) = delete; + AutoArrayPointer& operator=(const AutoArrayPointer&) = delete; + AutoArrayPointer& operator=(AutoArrayPointer&&) = delete; + + explicit AutoArrayPointer(int size) { data = new T[size]; } T& operator[](int pos) { return data[pos]; } operator T*() const { return data; } ~AutoArrayPointer() { delete[] data; } @@ -85,6 +90,10 @@ class AutoArrayPointer T* data; }; +typedef unsigned long long ThreadId; +LIBSHIBOKEN_API ThreadId currentThreadId(); +LIBSHIBOKEN_API ThreadId mainThreadId(); + /** * An utility function used to call PyErr_WarnEx with a formatted message. */ diff --git a/sources/shiboken2/libshiboken/pep384impl.cpp b/sources/shiboken2/libshiboken/pep384impl.cpp index 0baf6ed42..869d09529 100644 --- a/sources/shiboken2/libshiboken/pep384impl.cpp +++ b/sources/shiboken2/libshiboken/pep384impl.cpp @@ -398,7 +398,10 @@ PyRun_String(const char *str, int start, PyObject *globals, PyObject *locals) return ret; } +#endif // Py_LIMITED_API + // This is only a simple local helper that returns a computed variable. +// Used also in Python 2. static PyObject * PepRun_GetResult(const char *command, const char *resvar) { @@ -415,6 +418,8 @@ PepRun_GetResult(const char *command, const char *resvar) return res; } +#ifdef Py_LIMITED_API + /***************************************************************************** * * Support for classobject.h @@ -499,13 +504,26 @@ PyTypeObject *PepStaticMethod_TypePtr = NULL; static PyTypeObject *getStaticMethodType(void) { + // this works for Python 3, only + // "StaticMethodType = type(str.__dict__['maketrans'])\n"; static const char prog[] = - "StaticMethodType = type(str.__dict__['maketrans'])\n"; - return (PyTypeObject *) PepRun_GetResult(prog, "StaticMethodType"); + "from xxsubtype import spamlist\n" + "StaticMethod_Type = type(spamlist.__dict__['staticmeth'])\n"; + return (PyTypeObject *) PepRun_GetResult(prog, "StaticMethod_Type"); } - #endif // Py_LIMITED_API +#if PY_VERSION_HEX < 0x03000000 +PyTypeObject *PepMethodDescr_TypePtr = NULL; + +static PyTypeObject *getMethodDescrType(void) +{ + static const char prog[] = + "MethodDescr_Type = type(str.split)\n"; + return (PyTypeObject *) PepRun_GetResult(prog, "MethodDescr_Type"); +} +#endif + /***************************************************************************** * * Common newly needed functions @@ -630,6 +648,9 @@ Pep384_Init() PepFunction_TypePtr = getFunctionType(); PepStaticMethod_TypePtr = getStaticMethodType(); #endif +#if PY_VERSION_HEX < 0x03000000 + PepMethodDescr_TypePtr = getMethodDescrType(); +#endif } } // extern "C" diff --git a/sources/shiboken2/libshiboken/pep384impl.h b/sources/shiboken2/libshiboken/pep384impl.h index b566a6218..6649fa95e 100644 --- a/sources/shiboken2/libshiboken/pep384impl.h +++ b/sources/shiboken2/libshiboken/pep384impl.h @@ -286,7 +286,7 @@ LIBSHIBOKEN_API PyObject *PyRun_String(const char *, int, PyObject *, PyObject * // But this is no problem as we check it's validity for every version. #define PYTHON_BUFFER_VERSION_COMPATIBLE (PY_VERSION_HEX >= 0x03030000 && \ - PY_VERSION_HEX < 0X0307FFFF) + PY_VERSION_HEX < 0x0307FFFF) #if !PYTHON_BUFFER_VERSION_COMPATIBLE # error Please check the buffer compatibility for this python version! #endif @@ -470,6 +470,12 @@ extern LIBSHIBOKEN_API PyTypeObject *PepStaticMethod_TypePtr; #else #define PepStaticMethod_TypePtr &PyStaticMethod_Type #endif +// Although not PEP specific, we resolve this similar issue, here: +#if PY_VERSION_HEX < 0x03000000 +extern LIBSHIBOKEN_API PyTypeObject *PepMethodDescr_TypePtr; +#else +#define PepMethodDescr_TypePtr &PyMethodDescr_Type +#endif /***************************************************************************** * diff --git a/sources/shiboken2/libshiboken/sbkconverter.cpp b/sources/shiboken2/libshiboken/sbkconverter.cpp index a13222de6..65844151f 100644 --- a/sources/shiboken2/libshiboken/sbkconverter.cpp +++ b/sources/shiboken2/libshiboken/sbkconverter.cpp @@ -246,10 +246,8 @@ PythonToCppFunc isPythonToCppPointerConvertible(SbkObjectType *type, PyObject *p static inline PythonToCppFunc IsPythonToCppConvertible(const SbkConverter *converter, PyObject *pyIn) { assert(pyIn); - const ToCppConversionList& convs = converter->toCppConversions; - for (ToCppConversionList::const_iterator conv = convs.begin(), end = convs.end(); conv != end; ++conv) { - PythonToCppFunc toCppFunc = 0; - if ((toCppFunc = (*conv).first(pyIn))) + for (const ToCppConversion &c : converter->toCppConversions) { + if (PythonToCppFunc toCppFunc = c.first(pyIn)) return toCppFunc; } return 0; @@ -361,7 +359,7 @@ bool isImplicitConversion(SbkObjectType *type, PythonToCppFunc toCppFunc) // Note that we don't check if the Python to C++ conversion is in // the list of the type's conversions, for it is expected that the // caller knows what he's doing. - ToCppConversionList::iterator conv = PepType_SOTP(type)->converter->toCppConversions.begin(); + const auto conv = PepType_SOTP(type)->converter->toCppConversions.cbegin(); return toCppFunc != (*conv).second; } diff --git a/sources/shiboken2/libshiboken/sbkconverter_p.h b/sources/shiboken2/libshiboken/sbkconverter_p.h index f39608663..a4edfe81e 100644 --- a/sources/shiboken2/libshiboken/sbkconverter_p.h +++ b/sources/shiboken2/libshiboken/sbkconverter_p.h @@ -43,11 +43,11 @@ #include "sbkpython.h" #include "sbkconverter.h" #include "sbkstring.h" -#include <list> #include <limits> #include <typeinfo> #include <sstream> #include <iostream> +#include <vector> #include "sbkdbg.h" @@ -55,7 +55,7 @@ extern "C" { typedef std::pair<IsConvertibleToCppFunc, PythonToCppFunc> ToCppConversion; -typedef std::list<ToCppConversion> ToCppConversionList; +typedef std::vector<ToCppConversion> ToCppConversionVector; /** * \internal @@ -104,7 +104,7 @@ struct SbkConverter * For Object Types, that never have implicit conversions, this * list is always empty. */ - ToCppConversionList toCppConversions; + ToCppConversionVector toCppConversions; }; } // extern "C" diff --git a/sources/shiboken2/libshiboken/sbkdbg.h b/sources/shiboken2/libshiboken/sbkdbg.h index c26816bbd..fdaf2a27a 100644 --- a/sources/shiboken2/libshiboken/sbkdbg.h +++ b/sources/shiboken2/libshiboken/sbkdbg.h @@ -63,6 +63,11 @@ class BaseLogger { public: + BaseLogger(const BaseLogger&) = delete; + BaseLogger(BaseLogger&&) = delete; + BaseLogger& operator=(const BaseLogger&) = delete; + BaseLogger& operator=(BaseLogger&&) = delete; + BaseLogger(std::ostream& output, const char* function, const char* context) : m_stream(output), m_function(function), m_context(context) {} ~BaseLogger() diff --git a/sources/shiboken2/libshiboken/sbkenum.cpp b/sources/shiboken2/libshiboken/sbkenum.cpp index d129e6380..3c6582adc 100644 --- a/sources/shiboken2/libshiboken/sbkenum.cpp +++ b/sources/shiboken2/libshiboken/sbkenum.cpp @@ -47,7 +47,7 @@ #include <string.h> #include <cstring> -#include <list> +#include <vector> #define SBK_ENUM(ENUM) reinterpret_cast<SbkEnumObject*>(ENUM) @@ -339,15 +339,18 @@ namespace Shiboken { class DeclaredEnumTypes { public: + DeclaredEnumTypes(const DeclaredEnumTypes&) = delete; + DeclaredEnumTypes(DeclaredEnumTypes&&) = delete; + DeclaredEnumTypes& operator=(const DeclaredEnumTypes&) = delete; + DeclaredEnumTypes& operator=(DeclaredEnumTypes&&) = delete; + DeclaredEnumTypes(); ~DeclaredEnumTypes(); static DeclaredEnumTypes& instance(); void addEnumType(PyTypeObject* type); private: - DeclaredEnumTypes(const DeclaredEnumTypes&); - DeclaredEnumTypes& operator=(const DeclaredEnumTypes&); - std::list<PyTypeObject*> m_enumTypes; + std::vector<PyTypeObject *> m_enumTypes; }; namespace Enum { @@ -373,7 +376,9 @@ PyObject* getEnumItemFromValue(PyTypeObject* enumType, long itemValue) return 0; } -static PyTypeObject* createEnum(const char* fullName, const char* cppName, const char* shortName, PyTypeObject* flagsType) +static PyTypeObject* createEnum(const char* fullName, const char* cppName, + const char* /* shortName */, + PyTypeObject* flagsType) { PyTypeObject* enumType = newTypeWithName(fullName, cppName, flagsType); if (PyType_Ready(enumType) < 0) @@ -524,13 +529,8 @@ copyNumberMethods(PyTypeObject *flagsType, int *pidx) { int idx = *pidx; -#ifdef IS_PY3K -# define SLOT slot -#else -# define SLOT slot_ -#endif #define PUT_SLOT(name) \ - number_slots[idx].SLOT = (name); \ + number_slots[idx].slot = (name); \ number_slots[idx].pfunc = PyType_GetSlot(flagsType, (name)); \ ++idx; @@ -593,8 +593,8 @@ newTypeWithName(const char* name, newspec->flags = SbkNewType_spec.flags; // we must append all the number methods, so rebuild everything: int idx = 0; - while (SbkNewType_slots[idx].SLOT) { - newslots[idx].SLOT = SbkNewType_slots[idx].SLOT; + while (SbkNewType_slots[idx].slot) { + newslots[idx].slot = SbkNewType_slots[idx].slot; newslots[idx].pfunc = SbkNewType_slots[idx].pfunc; ++idx; } @@ -644,14 +644,10 @@ DeclaredEnumTypes& DeclaredEnumTypes::instance() return me; } -DeclaredEnumTypes::DeclaredEnumTypes() -{ -} +DeclaredEnumTypes::DeclaredEnumTypes() = default; DeclaredEnumTypes::~DeclaredEnumTypes() { - std::list<PyTypeObject*>::const_iterator it = m_enumTypes.begin(); - for (; it != m_enumTypes.end(); ++it) { /* * PYSIDE-595: This was "delete *it;" before introducing 'PyType_FromSpec'. * XXX what should I do now? @@ -660,8 +656,8 @@ DeclaredEnumTypes::~DeclaredEnumTypes() * So right now I am doing nothing. Surely wrong but no crash. * See also the comment in function 'createGlobalEnumItem'. */ - //fprintf(stderr, "ttt %d %s\n", Py_REFCNT(*it), *it->tp_name); - } + // for (PyTypeObject *o : m_enumTypes) + // fprintf(stderr, "ttt %d %s\n", Py_REFCNT(o), o->tp_name); m_enumTypes.clear(); } diff --git a/sources/shiboken2/libshiboken/sbkpython.h b/sources/shiboken2/libshiboken/sbkpython.h index 29e25605a..f06b0b19e 100644 --- a/sources/shiboken2/libshiboken/sbkpython.h +++ b/sources/shiboken2/libshiboken/sbkpython.h @@ -42,19 +42,65 @@ #include "sbkversion.h" +// Qt's "slots" macro collides with the "slots" member variables +// used in some Python structs. For compilers that support push_macro, +// temporarily undefine it. +#if defined(slots) && (defined(__GNUC__) || defined(_MSC_VER) || defined(__clang__)) +# pragma push_macro("slots") +# undef slots /* * Python 2 has function _Py_Mangle directly in Python.h . * This creates wrong language binding unless we define 'extern "C"' here. */ extern "C" { -#include <Python.h> +/* + * Python 2 uses the "register" keyword, which is deprecated in C++ 11 + * and forbidden in C++17. + */ +# if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdeprecated-register" +# endif + +# include <Python.h> + +# if defined(__clang__) +# pragma clang diagnostic pop +# endif } -#include <structmember.h> +# include <structmember.h> // Now we have the usual variables from Python.h . -#include "python25compat.h" -#include "shibokenmacros.h" -#include "pep384impl.h" -#include "typespec.h" +# include "python25compat.h" +# include "shibokenmacros.h" +# include "pep384impl.h" +# include "typespec.h" +# pragma pop_macro("slots") + +#else + +extern "C" { +/* + * Python 2 uses the "register" keyword, which is deprecated in C++ 11 + * and forbidden in C++17. + */ +# if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdeprecated-register" +# endif + +# include <Python.h> + +# if defined(__clang__) +# pragma clang diagnostic pop +# endif +} +# include <structmember.h> +// Now we have the usual variables from Python.h . +# include "python25compat.h" +# include "shibokenmacros.h" +# include "pep384impl.h" +# include "typespec.h" +#endif #if PY_MAJOR_VERSION >= 3 #define IS_PY3K diff --git a/sources/shiboken2/libshiboken/sbkstring.cpp b/sources/shiboken2/libshiboken/sbkstring.cpp index 6ca35f12e..a3ffcdabb 100644 --- a/sources/shiboken2/libshiboken/sbkstring.cpp +++ b/sources/shiboken2/libshiboken/sbkstring.cpp @@ -66,10 +66,7 @@ bool check(PyObject* obj) bool checkChar(PyObject* pyobj) { - if (check(pyobj) && (len(pyobj) == 1)) - return true; - - return false; + return check(pyobj) && (len(pyobj) == 1); } bool isConvertible(PyObject* obj) diff --git a/sources/shiboken2/libshiboken/shibokenbuffer.cpp b/sources/shiboken2/libshiboken/shibokenbuffer.cpp index dc29b40a7..a691a31ee 100644 --- a/sources/shiboken2/libshiboken/shibokenbuffer.cpp +++ b/sources/shiboken2/libshiboken/shibokenbuffer.cpp @@ -58,6 +58,7 @@ void* Shiboken::Buffer::getPointer(PyObject* pyObj, Py_ssize_t* size) PyBuffer_Release(&view); return view.buf; } + return nullptr; #else Py_ssize_t bufferSize = 0; @@ -85,7 +86,8 @@ PyObject* Shiboken::Buffer::newObject(void* memory, Py_ssize_t size, Type type) view.shape = shape; // Pep384: This is way too complicated and impossible with the limited api: //return PyMemoryView_FromBuffer(&view); - return PyMemoryView_FromMemory((char *)view.buf, size, type == ReadOnly ? PyBUF_READ : PyBUF_WRITE); + return PyMemoryView_FromMemory(reinterpret_cast<char *>(view.buf), + size, type == ReadOnly ? PyBUF_READ : PyBUF_WRITE); #else return type == ReadOnly ? PyBuffer_FromMemory(memory, size) : PyBuffer_FromReadWriteMemory(memory, size); #endif diff --git a/sources/shiboken2/libshiboken/signature.cpp b/sources/shiboken2/libshiboken/signature.cpp index f0bb8e609..24d60acc4 100644 --- a/sources/shiboken2/libshiboken/signature.cpp +++ b/sources/shiboken2/libshiboken/signature.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt for Python. @@ -77,18 +77,23 @@ typedef struct safe_globals_struc { static safe_globals pyside_globals = 0; -static PyObject *GetSignature_Function(PyCFunctionObject *); -static PyObject *GetSignature_TypeMod(PyObject *); +static PyObject *GetSignature_Function(PyCFunctionObject *, const char *); +static PyObject *GetSignature_TypeMod(PyObject *, const char *); +static PyObject *GetSignature_Wrapper(PyObject *, const char *); +static PyObject *get_signature(PyObject *self, PyObject *args); static PyObject *PySide_BuildSignatureProps(PyObject *class_mod); +static void init_module_1(void); +static void init_module_2(void); + const char helper_module_name[] = "signature_loader"; const char bootstrap_name[] = "bootstrap"; const char arg_name[] = "pyside_arg_dict"; const char func_name[] = "pyside_type_init"; static PyObject * -CreateSignature(PyObject *props, const char *sig_kind) +CreateSignature(PyObject *props, PyObject *key) { /* * Here is the new function to create all signatures. It simply calls @@ -97,22 +102,22 @@ CreateSignature(PyObject *props, const char *sig_kind) * to support '_signature_is_functionlike()'. */ return PyObject_CallFunction(pyside_globals->createsig_func, - (char *)"(Os)", props, sig_kind); + (char *)"(OO)", props, key); } static PyObject * -pyside_cf_get___signature__(PyObject *func) +pyside_cf_get___signature__(PyObject *func, const char *modifier) { - return GetSignature_Function((PyCFunctionObject *)func); + return GetSignature_Function((PyCFunctionObject *)func, modifier); } static PyObject * -pyside_sm_get___signature__(PyObject *sm) +pyside_sm_get___signature__(PyObject *sm, const char *modifier) { PyObject *func, *ret; func = PyObject_GetAttrString(sm, "__func__"); - ret = GetSignature_Function((PyCFunctionObject *)func); + ret = GetSignature_Function((PyCFunctionObject *)func, modifier); Py_XDECREF(func); return ret; } @@ -192,7 +197,7 @@ qualname_to_func(PyObject *ob) #endif static PyObject * -pyside_md_get___signature__(PyObject *ob) +pyside_md_get___signature__(PyObject *ob, const char *modifier) { PyObject *func; PyObject *result; @@ -218,21 +223,31 @@ pyside_md_get___signature__(PyObject *ob) return Py_None; if (func == NULL) Py_FatalError("missing mapping in MethodDescriptor"); - result = pyside_cf_get___signature__(func); + result = pyside_cf_get___signature__(func, modifier); Py_DECREF(func); return result; } static PyObject * -pyside_tp_get___signature__(PyObject *typemod) +pyside_wd_get___signature__(PyObject *ob, const char *modifier) +{ + return GetSignature_Wrapper(ob, modifier); +} + +static PyObject * +pyside_tp_get___signature__(PyObject *typemod, const char *modifier) { - return GetSignature_TypeMod(typemod); + return GetSignature_TypeMod(typemod, modifier); } +// forward +static PyObject * +GetSignature_Cached(PyObject *props, const char *sig_kind, const char *modifier); + static PyObject * -GetSignature_Function(PyCFunctionObject *func) +GetSignature_Function(PyCFunctionObject *func, const char *modifier) { - PyObject *typemod, *type_name, *dict, *props, *value, *selftype; + PyObject *typemod, *type_name, *dict, *props, *selftype; PyObject *func_name = PyObject_GetAttrString((PyObject *)func, "__name__"); const char *sig_kind; int flags; @@ -241,12 +256,8 @@ GetSignature_Function(PyCFunctionObject *func) if (selftype == NULL) selftype = PyDict_GetItem(pyside_globals->map_dict, (PyObject *)func); if (selftype == NULL) { - if (!PyErr_Occurred()) { - PyErr_Format(PyExc_SystemError, - "the signature for \"%s\" should exist", - PepCFunction_GET_NAMESTR(func) - ); - } + if (!PyErr_Occurred()) + Py_RETURN_NONE; return NULL; } if ((PyType_Check(selftype) || PyModule_Check(selftype))) @@ -279,24 +290,46 @@ GetSignature_Function(PyCFunctionObject *func) sig_kind = "staticmethod"; else sig_kind = "method"; - value = PyDict_GetItemString(props, sig_kind); - if (value == NULL) { - // we need to compute a signature object - value = CreateSignature(props, sig_kind); - if (value != NULL) { - if (PyDict_SetItemString(props, sig_kind, value) < 0) - return NULL; - } - else + return GetSignature_Cached(props, sig_kind, modifier); +} + +static PyObject * +GetSignature_Wrapper(PyObject *ob, const char *modifier) +{ + PyObject *dict, *props; + PyObject *func_name = PyObject_GetAttrString(ob, "__name__"); + PyObject *objclass = PyObject_GetAttrString(ob, "__objclass__"); + PyObject *class_name = PyObject_GetAttrString(objclass, "__name__"); + const char *sig_kind; + + if (func_name == nullptr || objclass == nullptr || class_name == nullptr) + return nullptr; + dict = PyDict_GetItem(pyside_globals->arg_dict, class_name); + if (dict == NULL) + Py_RETURN_NONE; + if (PyTuple_Check(dict)) { + /* + * We do the initialization lazily. + * This has also the advantage that we can freely import PySide. + */ + dict = PySide_BuildSignatureProps(objclass); + if (dict == NULL) Py_RETURN_NONE; } - return Py_INCREF(value), value; + props = PyDict_GetItem(dict, func_name); + Py_DECREF(func_name); + Py_DECREF(objclass); + Py_DECREF(class_name); + if (props == NULL) + Py_RETURN_NONE; + sig_kind = "method"; + return GetSignature_Cached(props, sig_kind, modifier); } static PyObject * -GetSignature_TypeMod(PyObject *ob) +GetSignature_TypeMod(PyObject *ob, const char *modifier) { - PyObject *ob_name, *dict, *props, *value; + PyObject *ob_name, *dict, *props; const char *sig_kind; ob_name = PyObject_GetAttrString(ob, "__name__"); @@ -314,37 +347,62 @@ GetSignature_TypeMod(PyObject *ob) if (props == NULL) Py_RETURN_NONE; sig_kind = "method"; - value = PyDict_GetItemString(props, sig_kind); - if (value == NULL) { + return GetSignature_Cached(props, sig_kind, modifier); +} + +static PyObject * +GetSignature_Cached(PyObject *props, const char *sig_kind, const char *modifier) +{ + PyObject *key, *value; + + if (modifier == nullptr) + key = Py_BuildValue("s", sig_kind); + else + key = Py_BuildValue("(ss)", sig_kind, modifier); + if (key == nullptr) + return nullptr; + value = PyDict_GetItem(props, key); + if (value == nullptr) { // we need to compute a signature object - value = CreateSignature(props, sig_kind); - if (value != NULL) { - if (PyDict_SetItemString(props, sig_kind, value) < 0) - return NULL; + value = CreateSignature(props, key); + if (value != nullptr) { + if (PyDict_SetItem(props, key, value) < 0) { + // this is an error + Py_DECREF(key); + return nullptr; + } } - else + else { + // key not found + Py_DECREF(key); Py_RETURN_NONE; + } } return Py_INCREF(value), value; } - static const char PySide_PythonCode[] = - "from __future__ import print_function, absolute_import\n" - "import sys, os, traceback\n" - - "pyside_package_dir = os.environ.get('PYSIDE_PACKAGE_DIR', '.')\n" - "__file__ = os.path.join(pyside_package_dir, 'support', 'signature', 'loader.py')\n" - - "def bootstrap():\n" - " try:\n" - " with open(__file__) as _f:\n" - " exec(compile(_f.read(), __file__, 'exec'))\n" - " except Exception as e:\n" - " print('Exception:', e)\n" - " traceback.print_exc(file=sys.stdout)\n" - " globals().update(locals())\n" - ; + "from __future__ import print_function, absolute_import\n" R"~(if True: + + import sys, os, traceback + + pyside_package_dir = os.environ.get('PYSIDE_PACKAGE_DIR') + if pyside_package_dir is None: + # This happens in shiboken running ctest. + from distutils.sysconfig import get_python_lib + pyside_package_dir = os.path.join(get_python_lib(), 'PySide2') + __file__ = os.path.join(pyside_package_dir, 'support', 'signature', 'loader.py') + + def bootstrap(): + try: + with open(__file__) as _f: + exec(compile(_f.read(), __file__, 'exec')) + except Exception as e: + print('Exception:', e) + traceback.print_exc(file=sys.stdout) + globals().update(locals()) + + )~"; static safe_globals_struc * init_phase_1(void) @@ -387,9 +445,10 @@ error: } static int -init_phase_2(safe_globals_struc *p) +init_phase_2(safe_globals_struc *p, PyMethodDef *methods) { - PyObject *bootstrap_func; + PyObject *bootstrap_func, *v = nullptr; + PyMethodDef *ml; bootstrap_func = PyObject_GetAttrString(p->helper_module, bootstrap_name); if (bootstrap_func == NULL) @@ -403,9 +462,22 @@ init_phase_2(safe_globals_struc *p) p->createsig_func = PyObject_GetAttrString(p->helper_module, "create_signature"); if (p->createsig_func == NULL) goto error; + + // The single function to be called, but maybe more to come. + for (ml = methods; ml->ml_name != NULL; ml++) { + v = PyCFunction_NewEx(ml, nullptr, nullptr); + if (v == nullptr) { + goto error; + } + if (PyObject_SetAttrString(p->helper_module, ml->ml_name, v) != 0) { + goto error; + } + Py_DECREF(v); + } return 0; error: + Py_XDECREF(v); PyErr_SetString(PyExc_SystemError, "could not initialize part 2"); return -1; } @@ -413,8 +485,11 @@ error: static int add_more_getsets(PyTypeObject *type, PyGetSetDef *gsp) { - PyObject *dict = type->tp_dict; + PyObject *dict; + assert(PyType_Check(type)); + PyType_Ready(type); + dict = type->tp_dict; for (; gsp->name != NULL; gsp++) { PyObject *descr; if (PyDict_GetItemString(dict, gsp->name)) @@ -463,6 +538,45 @@ static PyGetSetDef new_PyType_getsets[] = { {0} }; +static PyGetSetDef new_PyWrapperDescr_getsets[] = { + {(char *) "__signature__", (getter)pyside_wd_get___signature__}, + {0} +}; + +//////////////////////////////////////////////////////////////////////////// +// +// get_signature -- providing a superior interface +// +// Additionally to the interface via __signature__, we also provide +// a general function, which allows for different signature layouts. +// The "modifier" argument is a string that is passed in from loader.py . +// Configuration what the modifiers mean is completely in Python. +// + +static PyObject * +get_signature(PyObject *self, PyObject *args) +{ + PyObject *ob; + const char *modifier = nullptr; + + init_module_1(); + init_module_2(); + + if (!PyArg_ParseTuple(args, "O|s", &ob, &modifier)) + return NULL; + if (Py_TYPE(ob) == &PyCFunction_Type) + return pyside_cf_get___signature__(ob, modifier); + if (Py_TYPE(ob) == PepStaticMethod_TypePtr) + return pyside_sm_get___signature__(ob, modifier); + if (Py_TYPE(ob) == PepMethodDescr_TypePtr) + return pyside_md_get___signature__(ob, modifier); + if (PyType_Check(ob)) + return pyside_tp_get___signature__(ob, modifier); + if (Py_TYPE(ob) == &PyWrapperDescr_Type) + return pyside_wd_get___signature__(ob, modifier); + Py_RETURN_NONE; +} + //////////////////////////////////////////////////////////////////////////// // // This special Type_Ready does certain initializations earlier with @@ -497,23 +611,23 @@ void handler(int sig) { static int PySideType_Ready(PyTypeObject *type) { - PyObject *md; + PyObject *md, *wd; static int init_done = 0; if (!init_done) { - // Python2 does not expose certain types. We look them up: - // PyMethodDescr_Type 'type(str.__dict__["split"])' - // PyClassMethodDescr_Type. 'type(dict.__dict__["fromkeys"])' - // The latter is not needed until we use class methods in PySide. - md = PyObject_GetAttrString((PyObject *)&PyString_Type, "split"); - if (md == NULL + md = PyObject_GetAttrString((PyObject *)&PyString_Type, "split"); // method-descriptor + wd = PyObject_GetAttrString((PyObject *)Py_TYPE(Py_True), "__add__"); // wrapper-descriptor + if (md == nullptr || wd == nullptr || PyType_Ready(Py_TYPE(md)) < 0 - || add_more_getsets(Py_TYPE(md), new_PyMethodDescr_getsets) < 0 + || add_more_getsets(PepMethodDescr_TypePtr, new_PyMethodDescr_getsets) < 0 || add_more_getsets(&PyCFunction_Type, new_PyCFunction_getsets) < 0 || add_more_getsets(PepStaticMethod_TypePtr, new_PyStaticMethod_getsets) < 0 - || add_more_getsets(&PyType_Type, new_PyType_getsets) < 0) + || add_more_getsets(&PyType_Type, new_PyType_getsets) < 0 + || add_more_getsets(Py_TYPE(wd), new_PyWrapperDescr_getsets) < 0 + ) return -1; Py_DECREF(md); + Py_DECREF(wd); #ifndef _WIN32 // We enable the stack trace in CI, only. const char *testEnv = getenv("QTEST_ENVIRONMENT"); @@ -550,20 +664,26 @@ build_func_to_type(PyObject *obtype) return 0; } +static void +init_module_1(void) +{ + static int init_done = 0; + + if (!init_done) { + pyside_globals = init_phase_1(); + if (pyside_globals != nullptr) + init_done = 1; + } +} + static int PySide_BuildSignatureArgs(PyObject *module, PyObject *type, const char *signatures) { PyObject *type_name, *arg_tup; const char *name = NULL; - static int init_done = 0; - if (!init_done) { - pyside_globals = init_phase_1(); - if (pyside_globals == NULL) - return -1; - init_done = 1; - } + init_module_1();; arg_tup = Py_BuildValue("(Os)", type, signatures); if (arg_tup == NULL) return -1; @@ -599,23 +719,34 @@ PySide_BuildSignatureArgs(PyObject *module, PyObject *type, return 0; } -static PyObject * -PySide_BuildSignatureProps(PyObject *classmod) +static PyMethodDef signature_methods[] = { + {"get_signature", (PyCFunction)get_signature, METH_VARARGS, + "get the __signature__, but pass an optional string parameter"}, + {NULL, NULL} +}; + +static void +init_module_2(void) { - PyObject *arg_tup, *dict, *type_name; static int init_done = 0; if (!init_done) { - if (init_phase_2(pyside_globals) < 0) - return NULL; + init_phase_2(pyside_globals, signature_methods); init_done = 1; } +} + +static PyObject * +PySide_BuildSignatureProps(PyObject *classmod) +{ + PyObject *arg_tup, *dict, *type_name; /* * Here is the second part of the function. * This part will be called on-demand when needed by some attribute. * We simply pick up the arguments that we stored here and replace * them by the function result. */ + init_module_2(); type_name = PyObject_GetAttrString(classmod, "__name__"); if (type_name == NULL) return NULL; diff --git a/sources/shiboken2/libshiboken/signature.h b/sources/shiboken2/libshiboken/signature.h index 3a229cb5c..b65317662 100644 --- a/sources/shiboken2/libshiboken/signature.h +++ b/sources/shiboken2/libshiboken/signature.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt for Python. @@ -45,8 +45,8 @@ extern "C" { -LIBSHIBOKEN_API int SbkSpecial_Type_Ready(PyObject *, PyTypeObject *, const char*); -LIBSHIBOKEN_API void FinishSignatureInitialization(PyObject *, const char*); +LIBSHIBOKEN_API int SbkSpecial_Type_Ready(PyObject *, PyTypeObject *, const char *); //WS +LIBSHIBOKEN_API void FinishSignatureInitialization(PyObject *, const char *); } // extern "C" diff --git a/sources/shiboken2/libshiboken/threadstatesaver.h b/sources/shiboken2/libshiboken/threadstatesaver.h index e9f97f300..ddad9b67f 100644 --- a/sources/shiboken2/libshiboken/threadstatesaver.h +++ b/sources/shiboken2/libshiboken/threadstatesaver.h @@ -49,15 +49,17 @@ namespace Shiboken class LIBSHIBOKEN_API ThreadStateSaver { public: + ThreadStateSaver(const ThreadStateSaver&) = delete; + ThreadStateSaver(ThreadStateSaver&&) = delete; + ThreadStateSaver &operator=(const ThreadStateSaver&) = delete; + ThreadStateSaver &operator=(ThreadStateSaver&&) = delete; + ThreadStateSaver(); ~ThreadStateSaver(); void save(); void restore(); private: PyThreadState* m_threadState; - - ThreadStateSaver(const ThreadStateSaver&); - ThreadStateSaver& operator=(const ThreadStateSaver&); }; } // namespace Shiboken diff --git a/sources/shiboken2/libshiboken/typespec.cpp b/sources/shiboken2/libshiboken/typespec.cpp index d532c97ed..a67daf12d 100644 --- a/sources/shiboken2/libshiboken/typespec.cpp +++ b/sources/shiboken2/libshiboken/typespec.cpp @@ -37,6 +37,7 @@ ** ****************************************************************************/ +#include "sbkpython.h" #include "typespec.h" #include <structmember.h> @@ -514,7 +515,7 @@ best_base(PyObject *bases) } static const short slotoffsets[] = { - -1, /* invalid slot_ */ + -1, /* invalid slot */ /* Generated by typeslots.py */ 0, 0, @@ -603,7 +604,7 @@ PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases) PyObject *modname; char *s; char *res_start = (char*)res; - PyType_Slot *slot_; + PyType_Slot *slot; /* Set the type name and qualname */ s = (char *)strrchr(spec->name, '.'); // C++11 @@ -632,11 +633,11 @@ PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases) if (!bases) { base = &PyBaseObject_Type; /* See whether Py_tp_base(s) was specified */ - for (slot_ = spec->slots; slot_->slot_; slot_++) { - if (slot_->slot_ == Py_tp_base) - base = (PyTypeObject *)slot_->pfunc; // C++11 - else if (slot_->slot_ == Py_tp_bases) { - bases = (PyObject *)slot_->pfunc; // C++11 + for (slot = spec->slots; slot->slot; slot++) { + if (slot->slot == Py_tp_base) + base = (PyTypeObject *)slot->pfunc; // C++11 + else if (slot->slot == Py_tp_bases) { + bases = (PyObject *)slot->pfunc; // C++11 Py_INCREF(bases); } } @@ -676,23 +677,23 @@ PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases) type->tp_basicsize = spec->basicsize; type->tp_itemsize = spec->itemsize; - for (slot_ = spec->slots; slot_->slot_; slot_++) { - if (slot_->slot_ < 0 - || (size_t)slot_->slot_ >= Py_ARRAY_LENGTH(slotoffsets)) { - PyErr_SetString(PyExc_RuntimeError, "invalid slot_ offset"); + for (slot = spec->slots; slot->slot; slot++) { + if (slot->slot < 0 + || (size_t)slot->slot >= Py_ARRAY_LENGTH(slotoffsets)) { + PyErr_SetString(PyExc_RuntimeError, "invalid slot offset"); goto fail; } - if (slot_->slot_ == Py_tp_base || slot_->slot_ == Py_tp_bases) + if (slot->slot == Py_tp_base || slot->slot == Py_tp_bases) /* Processed above */ continue; - *(void**)(res_start + slotoffsets[slot_->slot_]) = slot_->pfunc; + *(void**)(res_start + slotoffsets[slot->slot]) = slot->pfunc; - /* need to make a copy of the docstring slot_, which usually + /* need to make a copy of the docstring slot, which usually points to a static string literal */ - if (slot_->slot_ == Py_tp_doc) { + if (slot->slot == Py_tp_doc) { // No signature in Python 2 - // const char *old_doc = _PyType_DocWithoutSignature(type->tp_name, slot_->pfunc); - const char *old_doc = (const char *)slot_->pfunc; + // const char *old_doc = _PyType_DocWithoutSignature(type->tp_name, slot->pfunc); + const char *old_doc = (const char *)slot->pfunc; size_t len = strlen(old_doc)+1; char *tp_doc = (char *)PyObject_MALLOC(len); // C++11 if (tp_doc == NULL) { @@ -759,17 +760,17 @@ PyType_FromSpec(PyType_Spec *spec) } void * -PyType_GetSlot(PyTypeObject *type, int slot_) +PyType_GetSlot(PyTypeObject *type, int slot) { - if (!PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE) || slot_ < 0) { + if (!PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE) || slot < 0) { PyErr_BadInternalCall(); return NULL; } - if ((size_t)slot_ >= Py_ARRAY_LENGTH(slotoffsets)) { - /* Extension module requesting slot_ from a future version */ + if ((size_t)slot >= Py_ARRAY_LENGTH(slotoffsets)) { + /* Extension module requesting slot from a future version */ return NULL; } - return *(void**)(((char*)type) + slotoffsets[slot_]); + return *(void**)(((char*)type) + slotoffsets[slot]); } } // extern "C" diff --git a/sources/shiboken2/libshiboken/typespec.h b/sources/shiboken2/libshiboken/typespec.h index 799fcb1b8..81227acac 100644 --- a/sources/shiboken2/libshiboken/typespec.h +++ b/sources/shiboken2/libshiboken/typespec.h @@ -40,7 +40,7 @@ #ifndef TYPESPEC_H #define TYPESPEC_H -#include <Python.h> +#include "sbkpython.h" #include "shibokenmacros.h" #if PY_MAJOR_VERSION < 3 @@ -48,7 +48,7 @@ extern "C" { typedef struct{ - int slot_; // slot is somehow reserved in Qt /* slot id, see below */ + int slot; // slot is somehow reserved in Qt /* slot id, see below */ void *pfunc; /* function pointer */ } PyType_Slot; diff --git a/sources/shiboken2/shiboken_tool.py b/sources/shiboken2/shiboken_tool.py new file mode 100755 index 000000000..8494c5d57 --- /dev/null +++ b/sources/shiboken2/shiboken_tool.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of Qt for Python. +## +## $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$ +## +############################################################################# +import sys +import os +import subprocess + +def main(): + # The tools listed as entrypoints in setup.py are copied to 'scripts/..' + cmd = os.path.join("..", os.path.basename(sys.argv[0])) + command = [os.path.join(os.path.dirname(os.path.realpath(__file__)), cmd)] + command.extend(sys.argv[1:]) + sys.exit(subprocess.call(command)) + +if __name__ == "__main__": + main() diff --git a/sources/shiboken2/shiboken_version.py b/sources/shiboken2/shiboken_version.py index 789812464..a883bab96 100644 --- a/sources/shiboken2/shiboken_version.py +++ b/sources/shiboken2/shiboken_version.py @@ -38,16 +38,10 @@ ############################################################################# major_version = "5" -minor_version = "11" -patch_version = "1" - -# For example: "a", "b", "rc" -# (which means "alpha", "beta", "release candidate"). -# An empty string means the generated package will be an official release. -pre_release_version_type = "a" - -# For example: "1", "2" (which means "beta1", "beta2", if type is "b"). -pre_release_version = "1" +minor_version = "12" +patch_version = "0" +pre_release_version_type = "a" # e.g. "a", "b", "rc". +pre_release_version = "1" # e.g "1", "2", (which means "beta1", "beta2", if type is "b") if __name__ == '__main__': # Used by CMake. diff --git a/sources/shiboken2/shibokenmodule/CMakeLists.txt b/sources/shiboken2/shibokenmodule/CMakeLists.txt index f2d7b30f2..20baf9f7e 100644 --- a/sources/shiboken2/shibokenmodule/CMakeLists.txt +++ b/sources/shiboken2/shibokenmodule/CMakeLists.txt @@ -12,7 +12,9 @@ set(shibokenmodule_TYPESYSTEM ${CMAKE_CURRENT_SOURCE_DIR}/typesystem_shiboken.xml ) -add_custom_command(OUTPUT ${sample_SRC} +add_custom_command( +OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/mjb_rejected_classes.log" +BYPRODUCTS ${sample_SRC} # Note: shiboken2 is an executable target. By not specifying its explicit # path, CMAKE figures it out, itself! # This fixes an issue with Visual Studio, see https://github.com/PySide/shiboken2/pull/11 @@ -39,5 +41,26 @@ target_link_libraries(shibokenmodule libshiboken) add_dependencies(shibokenmodule shiboken2) +create_generator_target(shibokenmodule) -install(TARGETS shibokenmodule DESTINATION ${PYTHON_SITE_PACKAGES}) +install(TARGETS shibokenmodule DESTINATION ${PYTHON_SITE_PACKAGES}/shiboken2) + +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/_config.py.in" + "${CMAKE_CURRENT_BINARY_DIR}/_config.py" @ONLY) +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/_config.py" + DESTINATION "${PYTHON_SITE_PACKAGES}/shiboken2") + +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/__init__.py.in" + "${CMAKE_CURRENT_BINARY_DIR}/__init__.py" @ONLY) +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/__init__.py" + DESTINATION "${PYTHON_SITE_PACKAGES}/shiboken2") + +# Use absolute path instead of relative path, to avoid ninja build errors due to +# duplicate file dependency inconsistency. +set(shiboken_version_relative_path "${CMAKE_CURRENT_SOURCE_DIR}/../shiboken_version.py") +get_filename_component(shiboken_version_path ${shiboken_version_relative_path} ABSOLUTE) +configure_file("${shiboken_version_path}" + "${CMAKE_CURRENT_BINARY_DIR}/_git_shiboken_module_version.py" @ONLY) + +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/_git_shiboken_module_version.py" + DESTINATION "${PYTHON_SITE_PACKAGES}/shiboken2") diff --git a/sources/shiboken2/shibokenmodule/__init__.py.in b/sources/shiboken2/shibokenmodule/__init__.py.in new file mode 100644 index 000000000..81ab0063a --- /dev/null +++ b/sources/shiboken2/shibokenmodule/__init__.py.in @@ -0,0 +1,4 @@ +__version__ = "@FINAL_PACKAGE_VERSION@" +__version_info__ = (@shiboken_MAJOR_VERSION@, @shiboken_MINOR_VERSION@, @shiboken_MICRO_VERSION@, "@shiboken_PRE_RELEASE_VERSION_TYPE@", "@shiboken_PRE_RELEASE_VERSION@") + +from .shiboken2 import * diff --git a/sources/shiboken2/shibokenmodule/_config.py.in b/sources/shiboken2/shibokenmodule/_config.py.in new file mode 100644 index 000000000..9607e5ca7 --- /dev/null +++ b/sources/shiboken2/shibokenmodule/_config.py.in @@ -0,0 +1,11 @@ +shiboken_library_soversion = str(@shiboken2_library_so_version@) + +version = "@FINAL_PACKAGE_VERSION@" +version_info = (@shiboken_MAJOR_VERSION@, @shiboken_MINOR_VERSION@, @shiboken_MICRO_VERSION@, "@shiboken_PRE_RELEASE_VERSION_TYPE@", "@shiboken_PRE_RELEASE_VERSION@") + +@PACKAGE_BUILD_DATE@ +@PACKAGE_BUILD_COMMIT_DATE@ +@PACKAGE_BUILD_COMMIT_HASH@ +@PACKAGE_BUILD_COMMIT_HASH_DESCRIBED@ +@PACKAGE_SETUP_PY_PACKAGE_TIMESTAMP_ASSIGNMENT@ +@PACKAGE_SETUP_PY_PACKAGE_VERSION_ASSIGNMENT@ diff --git a/sources/shiboken2/tests/libsample/CMakeLists.txt b/sources/shiboken2/tests/libsample/CMakeLists.txt index 7bbc0c3dd..ae3d40312 100644 --- a/sources/shiboken2/tests/libsample/CMakeLists.txt +++ b/sources/shiboken2/tests/libsample/CMakeLists.txt @@ -10,6 +10,7 @@ complex.cpp onlycopy.cpp derived.cpp echo.cpp +exceptiontest.cpp functions.cpp handle.cpp implicitconv.cpp diff --git a/sources/shiboken2/tests/libsample/exceptiontest.cpp b/sources/shiboken2/tests/libsample/exceptiontest.cpp new file mode 100644 index 000000000..1302a8e43 --- /dev/null +++ b/sources/shiboken2/tests/libsample/exceptiontest.cpp @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of Qt for Python. +** +** $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$ +** +****************************************************************************/ + +#include "exceptiontest.h" + +class TestException : public std::exception +{ +public: + const char *what() const noexcept override + { return "TestException"; } +}; + +ExceptionTest::ExceptionTest() = default; + +int ExceptionTest::intThrowStdException(bool doThrow) +{ + if (doThrow) + throw TestException(); + return 1; +} + +void ExceptionTest::voidThrowStdException(bool doThrow) +{ + if (doThrow) + throw TestException(); +} + +int ExceptionTest::intThrowInt(bool doThrow) +{ + if (doThrow) + throw 42; + return 1; +} + +void ExceptionTest::voidThrowInt(bool doThrow) +{ + if (doThrow) + throw 42; +} diff --git a/sources/shiboken2/tests/libsample/exceptiontest.h b/sources/shiboken2/tests/libsample/exceptiontest.h new file mode 100644 index 000000000..8ab3e2b67 --- /dev/null +++ b/sources/shiboken2/tests/libsample/exceptiontest.h @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of Qt for Python. +** +** $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$ +** +****************************************************************************/ + +#ifndef EXCEPTIONTEST_H +#define EXCEPTIONTEST_H + +#include "libsamplemacros.h" + +#include <exception> + +class LIBSAMPLE_API ExceptionTest +{ + public: + ExceptionTest(); + + int intThrowStdException(bool doThrow); + void voidThrowStdException(bool doThrow); + + int intThrowInt(bool doThrow); + void voidThrowInt(bool doThrow); +}; + +#endif // EXCEPTIONTEST_H diff --git a/sources/shiboken2/tests/libsample/mapuser.cpp b/sources/shiboken2/tests/libsample/mapuser.cpp index 1dbd02d26..89a835af8 100644 --- a/sources/shiboken2/tests/libsample/mapuser.cpp +++ b/sources/shiboken2/tests/libsample/mapuser.cpp @@ -67,3 +67,8 @@ MapUser::showMap(std::map<std::string, int> mapping) cout << (*it).first << " => " << (*it).second << endl; } +std::map<int, std::list<std::list<double> > > MapUser::foo() const +{ + std::map<int, std::list<std::list<double> > > result; + return result; +} diff --git a/sources/shiboken2/tests/libsample/mapuser.h b/sources/shiboken2/tests/libsample/mapuser.h index 9677d2df2..ad434b957 100644 --- a/sources/shiboken2/tests/libsample/mapuser.h +++ b/sources/shiboken2/tests/libsample/mapuser.h @@ -58,6 +58,8 @@ public: inline const std::map<int, ByteArray>& passMapIntValueType(const std::map<int, ByteArray>& arg) { return arg; } + std::map<int, std::list<std::list<double> > > foo() const; + private: std::map<std::string, std::list<int> > m_map; }; diff --git a/sources/shiboken2/tests/libsample/nontypetemplate.h b/sources/shiboken2/tests/libsample/nontypetemplate.h new file mode 100644 index 000000000..4e2100626 --- /dev/null +++ b/sources/shiboken2/tests/libsample/nontypetemplate.h @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of Qt for Python. +** +** $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$ +** +****************************************************************************/ + +#ifndef NONTYPETEMPLATE_H +#define NONTYPETEMPLATE_H + +#include "libsamplemacros.h" + +#include <algorithm> +#include <numeric> + +template <int Size> class IntArray +{ +public: + explicit IntArray(int v) { std::fill(m_array, m_array + Size, v); } + + int sum() const { return std::accumulate(m_array, m_array + Size, int(0)); } + +private: + int m_array[Size]; +}; + +typedef IntArray<2> IntArray2; +typedef IntArray<3> IntArray3; + +#endif // NONTYPETEMPLATE_H diff --git a/sources/shiboken2/tests/libsample/objecttype.cpp b/sources/shiboken2/tests/libsample/objecttype.cpp index e09a92f47..f82b7cf0d 100644 --- a/sources/shiboken2/tests/libsample/objecttype.cpp +++ b/sources/shiboken2/tests/libsample/objecttype.cpp @@ -282,6 +282,7 @@ void ObjectType::callVirtualCreateChild() ObjectType* fake_parent = new ObjectType(); ObjectType* fake_child = createChild(fake_parent); assert(fake_child->isPython()); + (void)fake_child; delete fake_parent; } diff --git a/sources/shiboken2/tests/libsample/objecttype.h b/sources/shiboken2/tests/libsample/objecttype.h index bcb4f3332..ecd67b684 100644 --- a/sources/shiboken2/tests/libsample/objecttype.h +++ b/sources/shiboken2/tests/libsample/objecttype.h @@ -63,7 +63,7 @@ private: class ObjectTypeLayout; class ObjectType; -typedef std::list<ObjectType*> ObjectTypeList; +using ObjectTypeList = std::list<ObjectType*>; class LIBSAMPLE_API ObjectType { diff --git a/sources/shiboken2/tests/libsample/samplenamespace.cpp b/sources/shiboken2/tests/libsample/samplenamespace.cpp index ec3da3dbf..e066869d2 100644 --- a/sources/shiboken2/tests/libsample/samplenamespace.cpp +++ b/sources/shiboken2/tests/libsample/samplenamespace.cpp @@ -36,6 +36,13 @@ using namespace std; namespace SampleNamespace { +// PYSIDE-817, scoped enums must not be converted to int in the wrappers generated +// for the protected hacks +SomeClass::PublicScopedEnum SomeClass::protectedMethodReturningPublicScopedEnum() const +{ + return PublicScopedEnum::v1; +} + OutValue enumInEnumOut(InValue in) { diff --git a/sources/shiboken2/tests/libsample/samplenamespace.h b/sources/shiboken2/tests/libsample/samplenamespace.h index 37a445e51..27fa11290 100644 --- a/sources/shiboken2/tests/libsample/samplenamespace.h +++ b/sources/shiboken2/tests/libsample/samplenamespace.h @@ -96,9 +96,11 @@ LIBSAMPLE_API void doSomethingWithArray(const unsigned char* data, unsigned int LIBSAMPLE_API int enumItemAsDefaultValueToIntArgument(int value = ZeroIn); -class SomeClass +class LIBSAMPLE_API SomeClass { public: + enum class PublicScopedEnum { v1, v2 }; + class SomeInnerClass { public: @@ -131,6 +133,8 @@ protected: ProtectedItem0, ProtectedItem1 }; + + PublicScopedEnum protectedMethodReturningPublicScopedEnum() const; }; class DerivedFromNamespace : public SomeClass::SomeInnerClass::OkThisIsRecursiveEnough diff --git a/sources/shiboken2/tests/minimalbinding/CMakeLists.txt b/sources/shiboken2/tests/minimalbinding/CMakeLists.txt index b8b6417d1..ec674b56b 100644 --- a/sources/shiboken2/tests/minimalbinding/CMakeLists.txt +++ b/sources/shiboken2/tests/minimalbinding/CMakeLists.txt @@ -15,7 +15,9 @@ ${CMAKE_CURRENT_BINARY_DIR}/minimal/minbooluser_wrapper.cpp configure_file("${CMAKE_CURRENT_SOURCE_DIR}/minimal-binding.txt.in" "${CMAKE_CURRENT_BINARY_DIR}/minimal-binding.txt" @ONLY) -add_custom_command(OUTPUT ${minimal_SRC} +add_custom_command( +OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/mjb_rejected_classes.log" +BYPRODUCTS ${minimal_SRC} COMMAND shiboken2 --project-file=${CMAKE_CURRENT_BINARY_DIR}/minimal-binding.txt ${GENERATOR_EXTRA_FLAGS} DEPENDS ${minimal_TYPESYSTEM} ${CMAKE_CURRENT_SOURCE_DIR}/global.h shiboken2 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} @@ -38,3 +40,4 @@ target_link_libraries(minimal libminimal ${SBK_PYTHON_LIBRARIES} libshiboken) +create_generator_target(minimal) diff --git a/sources/shiboken2/tests/otherbinding/CMakeLists.txt b/sources/shiboken2/tests/otherbinding/CMakeLists.txt index 186766b41..0be66f797 100644 --- a/sources/shiboken2/tests/otherbinding/CMakeLists.txt +++ b/sources/shiboken2/tests/otherbinding/CMakeLists.txt @@ -17,7 +17,9 @@ ${CMAKE_CURRENT_BINARY_DIR}/other/other_module_wrapper.cpp configure_file("${CMAKE_CURRENT_SOURCE_DIR}/other-binding.txt.in" "${CMAKE_CURRENT_BINARY_DIR}/other-binding.txt" @ONLY) -add_custom_command(OUTPUT ${other_SRC} +add_custom_command( +OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/mjb_rejected_classes.log" +BYPRODUCTS ${other_SRC} COMMAND shiboken2 --project-file=${CMAKE_CURRENT_BINARY_DIR}/other-binding.txt ${GENERATOR_EXTRA_FLAGS} DEPENDS ${other_TYPESYSTEM} ${CMAKE_CURRENT_SOURCE_DIR}/global.h shiboken2 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} @@ -48,4 +50,5 @@ target_link_libraries(other libshiboken) add_dependencies(other sample) +create_generator_target(other) diff --git a/sources/shiboken2/tests/samplebinding/CMakeLists.txt b/sources/shiboken2/tests/samplebinding/CMakeLists.txt index 32117e44a..7f4bec5f4 100644 --- a/sources/shiboken2/tests/samplebinding/CMakeLists.txt +++ b/sources/shiboken2/tests/samplebinding/CMakeLists.txt @@ -29,11 +29,14 @@ ${CMAKE_CURRENT_BINARY_DIR}/sample/derived_someinnerclass_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/echo_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/event_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/expression_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/exceptiontest_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/friendofonlycopy_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/handleholder_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/implicitconv_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/implicitbase_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/implicittarget_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/intarray2_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/intarray3_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/intlist_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/sortedoverload_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/intwrapper_wrapper.cpp @@ -125,7 +128,9 @@ ${CMAKE_CURRENT_BINARY_DIR}/sample/union_wrapper.cpp configure_file("${CMAKE_CURRENT_SOURCE_DIR}/sample-binding.txt.in" "${CMAKE_CURRENT_BINARY_DIR}/sample-binding.txt" @ONLY) -add_custom_command(OUTPUT ${sample_SRC} +add_custom_command( +OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/mjb_rejected_classes.log" +BYPRODUCTS ${sample_SRC} COMMAND shiboken2 --project-file=${CMAKE_CURRENT_BINARY_DIR}/sample-binding.txt ${GENERATOR_EXTRA_FLAGS} DEPENDS ${sample_TYPESYSTEM} ${CMAKE_CURRENT_SOURCE_DIR}/global.h shiboken2 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} @@ -149,3 +154,4 @@ target_link_libraries(sample libsample ${SBK_PYTHON_LIBRARIES} libshiboken) +create_generator_target(sample) diff --git a/sources/shiboken2/tests/samplebinding/exception_test.py b/sources/shiboken2/tests/samplebinding/exception_test.py new file mode 100644 index 000000000..d6c02433a --- /dev/null +++ b/sources/shiboken2/tests/samplebinding/exception_test.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python +# +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the test suite of Qt for Python. +## +## $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 sample import ExceptionTest + +class CppExceptionTest(unittest.TestCase): + + def testVoid(self): + exceptionCount = 0 + et = ExceptionTest() + + et.voidThrowStdException(False) + + try: + et.voidThrowStdException(True) + except: + exceptionCount += 1 + + et.voidThrowInt(False) + + try: + et.voidThrowInt(True) + except: + exceptionCount += 1 + + self.assertEqual(exceptionCount, 2) + + def testReturnValue(self): + exceptionCount = 0 + et = ExceptionTest() + + result = et.intThrowStdException(False); + + try: + result = et.intThrowStdException(True); + except: + exceptionCount += 1 + + result = et.intThrowInt(False); + + try: + result = et.intThrowInt(True); + except: + exceptionCount += 1 + + self.assertEqual(exceptionCount, 2) + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken2/tests/samplebinding/global.h b/sources/shiboken2/tests/samplebinding/global.h index f2add93ff..3984102a8 100644 --- a/sources/shiboken2/tests/samplebinding/global.h +++ b/sources/shiboken2/tests/samplebinding/global.h @@ -37,8 +37,10 @@ #include "sbkdate.h" #include "derived.h" #include "echo.h" +#include "exceptiontest.h" #include "functions.h" #include "implicitconv.h" +#include "nontypetemplate.h" #include "overloadsort.h" #include "handle.h" #include "injectcode.h" diff --git a/sources/shiboken2/tests/samplebinding/nontypetemplate_test.py b/sources/shiboken2/tests/samplebinding/nontypetemplate_test.py new file mode 100644 index 000000000..9adfa2441 --- /dev/null +++ b/sources/shiboken2/tests/samplebinding/nontypetemplate_test.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python +# +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the test suite of Qt for Python. +## +## $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 sample import IntArray2, IntArray3 + +class NonTypeTemplateTest(unittest.TestCase): + + def testNonTypeTemplate(self): + array2 = IntArray2(3) + self.assertEqual(array2.sum(), 6) + array3 = IntArray3(5) + self.assertEqual(array3.sum(), 15) + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken2/tests/samplebinding/typesystem_sample.xml b/sources/shiboken2/tests/samplebinding/typesystem_sample.xml index 00052e881..9b967fc53 100644 --- a/sources/shiboken2/tests/samplebinding/typesystem_sample.xml +++ b/sources/shiboken2/tests/samplebinding/typesystem_sample.xml @@ -520,6 +520,10 @@ <suppress-warning text="skipping function 'ClassWithFunctionPointer::callFunctionPointer', unmatched parameter type 'void (*)(void*)'" /> </value-type> + <value-type name="IntArray" generate="no"/> + <value-type name="IntArray2"/> + <value-type name="IntArray3"/> + <enum-type name="OverloadedFuncEnum"/> <!-- BUG: renaming the ICOverloadedFuncEnum to the same name @@ -545,6 +549,7 @@ <enum-type name="SampleNamespace"/> </object-type> <value-type name="SomeClass"> + <enum-type name="PublicScopedEnum"/> <value-type name="SomeInnerClass"> <object-type name="OkThisIsRecursiveEnough"> <enum-type name="NiceEnum" /> @@ -1964,7 +1969,7 @@ <define-ownership owner="c++"/> </modify-argument> </modify-function> - <modify-function signature="acceptSequence(const char*[])"> + <modify-function signature="acceptSequence(const char*const[])"> <modify-argument index="1"> <replace-type modified-type="PySequence" /> <conversion-rule class="native"> @@ -2388,6 +2393,8 @@ <value-type name="Expression" /> + <object-type name="ExceptionTest" exception-handling="auto-on"/> + <value-type name="ModelIndex" /> <value-type name="ReferentModelIndex"> <modify-function signature="operator const ModelIndex&()const"> diff --git a/sources/shiboken2/tests/smartbinding/CMakeLists.txt b/sources/shiboken2/tests/smartbinding/CMakeLists.txt index faaa797b6..43888fae2 100644 --- a/sources/shiboken2/tests/smartbinding/CMakeLists.txt +++ b/sources/shiboken2/tests/smartbinding/CMakeLists.txt @@ -16,7 +16,9 @@ ${CMAKE_CURRENT_BINARY_DIR}/smart/registry_wrapper.cpp configure_file("${CMAKE_CURRENT_SOURCE_DIR}/smart-binding.txt.in" "${CMAKE_CURRENT_BINARY_DIR}/smart-binding.txt" @ONLY) -add_custom_command(OUTPUT ${smart_SRC} +add_custom_command( +OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/mjb_rejected_classes.log" +BYPRODUCTS ${smart_SRC} COMMAND shiboken2 --project-file=${CMAKE_CURRENT_BINARY_DIR}/smart-binding.txt ${GENERATOR_EXTRA_FLAGS} DEPENDS ${smart_TYPESYSTEM} ${CMAKE_CURRENT_SOURCE_DIR}/global.h shiboken2 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} @@ -40,3 +42,4 @@ target_link_libraries(smart libsmart ${SBK_PYTHON_LIBRARIES} libshiboken) +create_generator_target(smart) diff --git a/testing/wheel_tester.py b/testing/wheel_tester.py new file mode 100644 index 000000000..60fd7a38a --- /dev/null +++ b/testing/wheel_tester.py @@ -0,0 +1,295 @@ +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of Qt for Python. +## +## $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$ +## +############################################################################# + +""" +This script is used by Coin (coin_test_instructions.py specifically) to +test installation of generated wheels, and test building of the +"buildable" examples samplebinding and scriptableapplication. + +It can also be invoked regularly from the command line via +python testing/wheel_tester.py --qmake=some-value --cmake=some-value + +The qmake and cmake arguments can also be omitted, and they will be +looked up in your PATH. + +Make sure that some generated wheels already exist in the dist/ +directory (e.g. setup.py bdist_wheel was already executed). +""" + +import os, sys + +try: + this_file = __file__ +except NameError: + this_file = sys.argv[0] +this_file = os.path.abspath(this_file) +this_dir = os.path.dirname(this_file) +setup_script_dir = os.path.abspath(os.path.join(this_dir, '..')) +sys.path.append(setup_script_dir) + +from build_scripts.options import OPTION_QMAKE +from build_scripts.options import OPTION_CMAKE + +from build_scripts.utils import find_files_using_glob +from build_scripts.utils import find_glob_in_path +from build_scripts.utils import run_process +from build_scripts.utils import rmtree +import distutils.log as log + +log.set_verbosity(1) + + +def find_executable_qmake(): + return find_executable('qmake', OPTION_QMAKE) + + +def find_executable_cmake(): + return find_executable('cmake', OPTION_CMAKE) + + +def find_executable(executable, command_line_value): + value = command_line_value + option_str = '--{}'.format(executable) + + if value: + log.info("{} option given: {}".format(option_str, value)) + if not os.path.exists(value): + raise RuntimeError("No executable exists at: {}".format(value)) + else: + log.info("No {} option given, trying to find {} in PATH.".format(option_str, executable)) + paths = find_glob_in_path(executable) + log.info("{} executables found in PATH: {}".format(executable, paths)) + if not paths: + raise RuntimeError( + "No {} option was specified and no {} was found " + "in PATH.".format(option_str, executable)) + else: + value = paths[0] + log.info("Using {} found in PATH: {}".format(executable, value)) + log.info("") + return value + + +QMAKE_PATH = find_executable_qmake() +CMAKE_PATH = find_executable_cmake() + + +def get_wheels_dir(): + return os.path.join(setup_script_dir, "dist") + + +def get_examples_dir(): + return os.path.join(setup_script_dir, "examples") + + +def package_prefix_names(): + return ["shiboken2", "shiboken2_generator", "PySide2"] + + +def clean_egg_info(): + # After a successful bdist_wheel build, some .egg-info directories + # are left over, which confuse pip when invoking it via + # python -m pip, making pip think that the packages are already + # installed in the root source directory. + # Clean up the .egg-info directories to fix this, it should be + # safe to do so. + paths = find_files_using_glob(setup_script_dir, "*.egg-info") + for p in paths: + log.info("Removing {}".format(p)) + rmtree(p) + + +def install_wheel(wheel_path): + log.info("Installing wheel: {}".format(wheel_path)) + exit_code = run_process([sys.executable, "-m", "pip", "install", wheel_path]) + log.info("") + if exit_code: + raise RuntimeError("Error while installing wheel {}".format(wheel_path)) + + +def try_install_wheels(wheels_dir, py_version): + clean_egg_info() + all_wheels_pattern = "*.whl" + all_wheels = find_files_using_glob(wheels_dir, all_wheels_pattern) + + if len(all_wheels) > 1: + log.info("Found the following wheels in {}: ".format(wheels_dir)) + for wheel in all_wheels: + log.info(wheel) + else: + log.info("No wheels found in {}".format(wheels_dir)) + log.info("") + + for p in package_prefix_names(): + pattern = "{}-*cp{}*.whl".format(p, py_version) + files = find_files_using_glob(wheels_dir, pattern) + if files and len(files) == 1: + wheel_path = files[0] + install_wheel(wheel_path) + elif len(files) > 1: + raise RuntimeError("More than one wheel found for specific package and version.") + else: + raise RuntimeError("No wheels compatible with Python {} found " + "for testing.".format(py_version)) + + +def is_unix(): + if sys.platform.startswith("linux") or sys.platform == "darwin": + return True + return False + + +def generate_build_cmake(): + args = [CMAKE_PATH] + if is_unix(): + args.extend(["-G", "Unix Makefiles"]) + else: + args.extend(["-G", "NMake Makefiles"]) + args.append("-DCMAKE_BUILD_TYPE=Release") + args.append("-Dpython_interpreter={}".format(sys.executable)) + + # Specify prefix path so find_package(Qt5) works. + qmake_dir = os.path.abspath(os.path.join(os.path.dirname(QMAKE_PATH), "..")) + args.append("-DCMAKE_PREFIX_PATH={}".format(qmake_dir)) + + args.append("..") + + exit_code = run_process(args) + if exit_code: + raise RuntimeError("Failure while running cmake.") + log.info("") + + +def generate_build_qmake(): + exit_code = run_process([QMAKE_PATH, "..", "python_interpreter={}".format(sys.executable)]) + if exit_code: + raise RuntimeError("Failure while running qmake.") + log.info("") + + +def run_make(): + args = [] + if is_unix(): + executable = "make" + else: + executable = "nmake" + args.append(executable) + + exit_code = run_process(args) + if exit_code: + raise RuntimeError("Failure while running {}.".format(executable)) + log.info("") + + +def run_make_install(): + args = [] + if is_unix(): + executable = "make" + else: + executable = "nmake" + args.append(executable) + args.append("install") + + exit_code = run_process(args) + if exit_code: + raise RuntimeError("Failed while running {} install.".format(executable)) + log.info("") + + +def execute_script(script_path): + args = [sys.executable, script_path] + exit_code = run_process(args) + if exit_code: + raise RuntimeError("Failure while executing script: {}".format(script_path)) + log.info("") + + +def prepare_build_folder(src_path, build_folder_name): + build_path = os.path.join(src_path, build_folder_name) + + # The script can be called for both Python 2 and Python 3 wheels, so + # preparing a build folder should clean any previous existing build. + if os.path.exists(build_path): + log.info("Removing {}".format(build_path)) + rmtree(build_path) + + log.info("Creating {}".format(build_path)) + os.makedirs(build_path) + os.chdir(build_path) + + +def try_build_examples(): + examples_dir = get_examples_dir() + + log.info("Attempting to build and run samplebinding using cmake.") + src_path = os.path.join(examples_dir, "samplebinding") + prepare_build_folder(src_path, "cmake") + generate_build_cmake() + run_make() + run_make_install() + execute_script(os.path.join(src_path, "main.py")) + + log.info("Attempting to build scriptableapplication using cmake.") + src_path = os.path.join(examples_dir, "scriptableapplication") + prepare_build_folder(src_path, "cmake") + generate_build_cmake() + run_make() + + log.info("Attempting to build scriptableapplication using qmake.") + src_path = os.path.join(examples_dir, "scriptableapplication") + prepare_build_folder(src_path, "qmake") + generate_build_qmake() + run_make() + + +def run_wheel_tests(): + wheels_dir = get_wheels_dir() + py_version = sys.version_info[0] + + log.info("Attempting to install wheels.\n") + try_install_wheels(wheels_dir, py_version) + + log.info("Attempting to build examples.\n") + try_build_examples() + + log.info("All tests passed!") + + +if __name__ == "__main__": + run_wheel_tests() |