diff --git a/doc/examples/accelbubble/Bubble.qml b/doc/examples/accelbubble/Bubble.qml new file mode 100644 index 00000000000..a7bbf63e0a9 --- /dev/null +++ b/doc/examples/accelbubble/Bubble.qml @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.2 + +Image { + source: "Bluebubble.svg" + smooth: true + property real centerX + property real centerY + property real bubbleCenter +} diff --git a/doc/examples/accelbubble/MainForm.ui.qml b/doc/examples/accelbubble/MainForm.ui.qml new file mode 100644 index 00000000000..345fa555b3c --- /dev/null +++ b/doc/examples/accelbubble/MainForm.ui.qml @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.2 + +Item { + id: id + width: 640 + height: 480 + property alias bubble: bubble + property alias mainWindow: mainWindow + + Rectangle { + id: mainWindow + color: "#ffffff" + anchors.rightMargin: 0 + anchors.bottomMargin: 0 + anchors.leftMargin: 0 + anchors.topMargin: 0 + anchors.fill: parent + } + + Bubble { + id: bubble + x: 10 + y: 18 + } + +} diff --git a/doc/examples/accelbubble/accelbubble.pro b/doc/examples/accelbubble/accelbubble.pro index 8dcac29df2b..5f1734b9eee 100644 --- a/doc/examples/accelbubble/accelbubble.pro +++ b/doc/examples/accelbubble/accelbubble.pro @@ -1,6 +1,8 @@ TEMPLATE = app -QT += qml quick widgets +QT += qml quick sensors svg xml + +CONFIG += c++11 SOURCES += main.cpp @@ -11,4 +13,3 @@ QML_IMPORT_PATH = # Default rules for deployment. include(deployment.pri) - diff --git a/doc/examples/accelbubble/main.cpp b/doc/examples/accelbubble/main.cpp new file mode 100644 index 00000000000..1f6684a668c --- /dev/null +++ b/doc/examples/accelbubble/main.cpp @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +int main(int argc, char *argv[]) +{ + QGuiApplication app(argc, argv); + + QQmlApplicationEngine engine; + engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); + + return app.exec(); +} diff --git a/doc/examples/accelbubble/main.qml b/doc/examples/accelbubble/main.qml index 02cc2f653d0..3b75f6664ea 100644 --- a/doc/examples/accelbubble/main.qml +++ b/doc/examples/accelbubble/main.qml @@ -1,10 +1,11 @@ -/************************************************************************** +/**************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing +** Contact: http://www.qt.io/licensing/ ** -** This file is part of Qt Creator +** This file is part of the examples of the Qt Toolkit. ** +** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without @@ -16,10 +17,10 @@ ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. -** * Neither the name of The Qt Company Ltd and its Subsidiary(-ies) nor -** the names of its contributors may be used to endorse or promote -** products derived from this software without specific prior written -** permission. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -33,90 +34,80 @@ ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** -**************************************************************************/ +** $QT_END_LICENSE$ +** +****************************************************************************/ import QtQuick 2.5 import QtQuick.Controls 1.4 - -import QtSensors 5.0 +import QtQuick.Dialogs 1.2 +import QtSensors 5.5 ApplicationWindow { - title: qsTr("Accelerate Bubble") id: mainWindow + visible: true width: 640 height: 480 - visible: true + title: qsTr("Accelerate Bubble") - menuBar: MenuBar { - Menu { - title: qsTr("File") - MenuItem { - text: qsTr("&Open") - onTriggered: console.log("Open action triggered"); - } - MenuItem { - text: qsTr("Exit") - onTriggered: Qt.quit(); - } - } - } + MainForm { + anchors.fill: parent + bubble { + id: bubble + centerX: mainWindow.width / 2 + centerY: mainWindow.height / 2 + bubbleCenter: bubble.width / 2 + x: bubble.centerX - bubble.bubbleCenter + y: bubble.centerY - bubble.bubbleCenter - Image { - id: bubble - source: "Bluebubble.svg" - smooth: true - property real centerX: mainWindow.width / 2 - property real centerY: mainWindow.height / 2 - property real bubbleCenter: bubble.width / 2 - x: centerX - bubbleCenter - y: centerY - bubbleCenter - - Behavior on y { - SmoothedAnimation { - easing.type: Easing.Linear - duration: 100 + Behavior on y { + SmoothedAnimation { + easing.type: Easing.Linear + duration: 100 } } - Behavior on x { - SmoothedAnimation { - easing.type: Easing.Linear - duration: 100 + Behavior on x { + SmoothedAnimation { + easing.type: Easing.Linear + duration: 100 } } - } - Accelerometer { - id: accel - dataRate: 100 - active:true - - onReadingChanged: { - var newX = (bubble.x + calcRoll(accel.reading.x, accel.reading.y, accel.reading.z) * 0.1) - var newY = (bubble.y - calcPitch(accel.reading.x, accel.reading.y, accel.reading.z) * 0.1) - - if (isNaN(newX) || isNaN(newY)) - return; - - if (newX < 0) - newX = 0 - - if (newX > mainWindow.width - bubble.width) - newX = mainWindow.width - bubble.width - - if (newY < 18) - newY = 18 - - if (newY > mainWindow.height - bubble.height) - newY = mainWindow.height - bubble.height - - bubble.x = newX - bubble.y = newY } - } - - function calcPitch(x, y, z) { - return -(Math.atan(y / Math.sqrt(x * x + z * z)) * 57.2957795); - } - function calcRoll(x, y, z) { - return -(Math.atan(x / Math.sqrt(y * y + z * z)) * 57.2957795); - } +} + +Accelerometer { + id: accel + dataRate: 100 + active: true + + onReadingChanged: { + var newX = (bubble.x + calcRoll(accel.reading.x, accel.reading.y, accel.reading.z) * 0.1) + var newY = (bubble.y - calcPitch(accel.reading.x, accel.reading.y, accel.reading.z) * 0.1) + + if (isNaN(newX) || isNaN(newY)) + return; + + if (newX < 0) + newX = 0 + + if (newX > mainWindow.width - bubble.width) + newX = mainWindow.width - bubble.width + + if (newY < 18) + newY = 18 + + if (newY > mainWindow.height - bubble.height) + newY = mainWindow.height - bubble.height + + bubble.x = newX + bubble.y = newY + } + } + + function calcPitch(x, y, z) { + return -(Math.atan(y / Math.sqrt(x * x + z * z)) * 57.2957795); + } + function calcRoll(x, y, z) { + return -(Math.atan(x / Math.sqrt(y * y + z * z)) * 57.2957795); + } } diff --git a/doc/examples/accelbubble/qml.qrc b/doc/examples/accelbubble/qml.qrc new file mode 100644 index 00000000000..21530e70397 --- /dev/null +++ b/doc/examples/accelbubble/qml.qrc @@ -0,0 +1,8 @@ + + + main.qml + MainForm.ui.qml + Bubble.qml + Bluebubble.svg + + diff --git a/doc/images/qmldesigner-borderimage-bindings-text.png b/doc/images/qmldesigner-borderimage-bindings-text.png new file mode 100644 index 00000000000..6484c13f014 Binary files /dev/null and b/doc/images/qmldesigner-borderimage-bindings-text.png differ diff --git a/doc/images/qmldesigner-borderimage-bindings.png b/doc/images/qmldesigner-borderimage-bindings.png new file mode 100644 index 00000000000..78a82b7f564 Binary files /dev/null and b/doc/images/qmldesigner-borderimage-bindings.png differ diff --git a/doc/images/qmldesigner-borderimage-connections.png b/doc/images/qmldesigner-borderimage-connections.png new file mode 100644 index 00000000000..e37adb69240 Binary files /dev/null and b/doc/images/qmldesigner-borderimage-connections.png differ diff --git a/doc/images/qmldesigner-borderimage-properties.png b/doc/images/qmldesigner-borderimage-properties.png new file mode 100644 index 00000000000..1a51b61a25d Binary files /dev/null and b/doc/images/qmldesigner-borderimage-properties.png differ diff --git a/doc/images/qtcreator-application-output.png b/doc/images/qtcreator-application-output.png index 0b191313243..5bd43831d5f 100644 Binary files a/doc/images/qtcreator-application-output.png and b/doc/images/qtcreator-application-output.png differ diff --git a/doc/images/qtcreator-build-dependencies.png b/doc/images/qtcreator-build-dependencies.png index 97b35971be0..e3d00f60be1 100644 Binary files a/doc/images/qtcreator-build-dependencies.png and b/doc/images/qtcreator-build-dependencies.png differ diff --git a/doc/images/qtcreator-compile-pane.png b/doc/images/qtcreator-compile-pane.png index 1b093555625..bfc4eb5693c 100644 Binary files a/doc/images/qtcreator-compile-pane.png and b/doc/images/qtcreator-compile-pane.png differ diff --git a/doc/src/editors/creator-clang-codemodel.qdoc b/doc/src/editors/creator-clang-codemodel.qdoc index fedc2998041..94d2bd4de94 100644 --- a/doc/src/editors/creator-clang-codemodel.qdoc +++ b/doc/src/editors/creator-clang-codemodel.qdoc @@ -142,7 +142,7 @@ \li Select \uicontrol Tools > \uicontrol Options > \uicontrol {C++} > \uicontrol {Code Model}, - and select the parser to use for files of each type. + and select \uicontrol {Use Clang Code Model}. \li To process pre-compiled headers before processing any project files, deselect the \uicontrol {Ignore pre-compiled headers} check box. diff --git a/doc/src/editors/creator-editors.qdoc b/doc/src/editors/creator-editors.qdoc index b5daf8067d0..c6df4082e45 100644 --- a/doc/src/editors/creator-editors.qdoc +++ b/doc/src/editors/creator-editors.qdoc @@ -1510,7 +1510,7 @@ defined file pattern only in the project you are currently editing. - \li \uicontrol {Files on File System} recursively searches files + \li \uicontrol {Files in File System} recursively searches files matching the defined file pattern in the selected directory. \li \uicontrol {Current File} searches only the current file. diff --git a/doc/src/howto/creator-ui.qdoc b/doc/src/howto/creator-ui.qdoc index c0c5c1ecb02..b3dbc977f17 100644 --- a/doc/src/howto/creator-ui.qdoc +++ b/doc/src/howto/creator-ui.qdoc @@ -354,7 +354,8 @@ To search within the \uicontrol{Application Output} and \uicontrol{Compile Output} panes, press \key {Ctrl+F} when the pane is active. Enter search criteria in the \uicontrol Find field and click the left and right arrows to search down and - up in the pane. + up in the pane. In these panes, you can also use the zoom buttons to increase and + decrease the text size of the output. To open the \uicontrol{General Messages} and \l{Using Version Control Systems} {Version Control} panes, select diff --git a/doc/src/projects/creator-projects-building.qdoc b/doc/src/projects/creator-projects-building.qdoc index be68e1e0b7e..4bb632fbad3 100644 --- a/doc/src/projects/creator-projects-building.qdoc +++ b/doc/src/projects/creator-projects-building.qdoc @@ -72,6 +72,12 @@ \uicontrol {Clean Project}. To clean the build directory and then build the project, select \uicontrol Build > \uicontrol {Rebuild All} or \uicontrol {Rebuild Project}. + To build and clean projects without dependencies, select the + \uicontrol {Build Without Dependencies}, + \uicontrol {Rebuild Without Dependencies}, and + \uicontrol {Clean Without Dependencies} options in the context menu in the + \uicontrol Projects view. + To run qmake to generate new Makefiles, select \uicontrol Build > \uicontrol qmake. */ diff --git a/doc/src/projects/creator-projects-cmake.qdoc b/doc/src/projects/creator-projects-cmake.qdoc index 4561f3f6b3a..0a20a00716b 100644 --- a/doc/src/projects/creator-projects-cmake.qdoc +++ b/doc/src/projects/creator-projects-cmake.qdoc @@ -93,20 +93,39 @@ Normally, there is no need to pass any command line arguments for projects that are already built, as CMake caches that information. + \section1 Editing CMake Configuration Files + + You can open CMake configuration files in \QC for editing. The following + features are supported: + + \list + + \li Pressing \key F2 when the cursor is on a filename to open the file + + \li Keyword completion + + \li Auto-indentation + + \li Matching parentheses and quotes + + \endlist + \section1 Building CMake Projects \QC builds CMake projects by running \c make, \c mingw32-make, \c nmake, or \c ninja depending on your platform. The build errors and warnings are parsed and displayed in the \uicontrol Issues output pane. - By default, \QC builds the \b{all} target. You can specify which - targets to build in \uicontrol Projects mode, under - \uicontrol {Build Settings}. + By default, \QC uses the \uicontrol All \l{glossary-build-config} + {build configuration}. You can select another build configuration in + \uicontrol Projects > \uicontrol {Edit build configuration}. In addition to + debug and release build configurations, you can create a release build that + contains debug information or a release build with the smallest possible + size. \image qtcreator-cmake-build-settings.png - \QC supports multiple build configurations. You can change the build - directory after the initial import. + You can change the build directory after the initial import. The build and run kit that you select determines which CMake tool is used for building. For more information, see \l {Adding Kits}. diff --git a/doc/src/projects/creator-projects-settings-dependencies.qdoc b/doc/src/projects/creator-projects-settings-dependencies.qdoc index e5561017012..c3a5fc0389f 100644 --- a/doc/src/projects/creator-projects-settings-dependencies.qdoc +++ b/doc/src/projects/creator-projects-settings-dependencies.qdoc @@ -51,6 +51,11 @@ \li Select projects that must be built before the current project is built. + \li Select the \uicontrol {Synchronize configuration} check box to + use the same kit as well as the same build and deploy + configuration to build and deploy all dependent projects loaded + in a session. + \endlist \QC calculates the build order based on the dependencies that you diff --git a/doc/src/qtquick/creator-mobile-app-tutorial.qdoc b/doc/src/qtquick/creator-mobile-app-tutorial.qdoc index 2110e822d58..ef74e9ccfe5 100644 --- a/doc/src/qtquick/creator-mobile-app-tutorial.qdoc +++ b/doc/src/qtquick/creator-mobile-app-tutorial.qdoc @@ -57,113 +57,122 @@ Program certificate that you receive from Apple. For more information, see \l{Connecting iOS Devices}. - \section1 Creating the Project + \include creator-tutorial-create-qq-project.qdocinc - \list 1 + \section1 Creating the Accelbubble Main View - \li Select \uicontrol File > \uicontrol {New File or Project} > - \uicontrol Application > \uicontrol {Qt Quick Controls Application} - > \uicontrol Choose. - - \li In the \uicontrol Name field, type \e {accelbubble}. - - \li In the \uicontrol {Create in} field, enter the path for the project - files, and then click \uicontrol Next (or \uicontrol Continue on - OS X). - - \li In the \uicontrol {Minimal required Qt version} field, select the Qt - version to develop with. - - \note This page determines the set of files that the wizard - generates and their contents. The instructions in this tutorial - might not apply if you select the \uicontrol {With .ui.qml file} - check box. - - \li Select \l{glossary-buildandrun-kit}{kits} for Android ARM and iPhone - OS, and click \uicontrol{Next}. - - \note Kits are listed if they have been specified in \uicontrol Tools > - \uicontrol Options > \uicontrol {Build & Run} > \uicontrol Kits (on Windows and Linux) - or in \uicontrol {Qt Creator} > \uicontrol Preferences \uicontrol {Build & Run} > - \uicontrol Kits (on OS X). - - \li Select \uicontrol Next in the following dialogs to use the default - settings. - - \li Review the project settings, and click \uicontrol{Finish} (or \uicontrol Done on - OS X). - - \endlist - - \QC generates a default QML file that you can modify to create the main view - of the application. - - \section1 Creating the Main View - - The main view of the application displays an SVG bubble image at the center - of the main window. + The main view of the application displays an SVG bubble image that moves + around the screen when you tilt the device. To use \l{accelbubble/Bluebubble.svg}{Bluebubble.svg} in your project, copy it to the project directory (same subdirectory as the QML file). The image appears in \uicontrol Resources. You can also use any other image or a QML type, instead. + To create the UI in the \uicontrol Design mode: + \list 1 - \li In the \uicontrol Projects view, double-click the main.qml file - to open it in the code editor. + \li In the \uicontrol Projects view, double-click the MainForm.ui.qml + file to open it in \QMLD. - \li Click \uicontrol Design to open the file in \QMLD. - - \li In the \uicontrol Navigator, select \uicontrol Label and press + \li In the \uicontrol Navigator, select \uicontrol RowLayout and press \key Delete to delete it. - \li Select \uicontrol ApplicationWindow to edit its properties. + \li In \uicontrol Library > \uicontrol {QML Types}, select + \uicontrol Rectangle and drag and drop it to the \uicontrol Item in + the navigator. - \list a + \li Select the rectangle in the navigator to edit its properties: - \li In the \uicontrol Id field, enter \e mainWindow, to be able to - reference the window from other places. + \list a - \li In the \uicontrol Title field, type \e {Accelerate Bubble}. + \li In the \uicontrol Id field enter \e mainWindow, to be able + to reference the rectangle from other places. - \endlist + \li Select the \uicontrol Layout tab, and then click + the \inlineimage qmldesigner-anchor-fill-screen.png + (\uicontrol {Fill to Parent}) button to anchor the rectangle + to the item. + + \endlist \li In \uicontrol Library > \uicontrol Resources, select Bluebubble.svg - and drag and drop it to the canvas. + and drag and drop it to \e mainWindow in the navigator. - \li In the \uicontrol Properties pane, \uicontrol Id field, enter \e bubble to be - able to reference the image from other places. + \li In the \uicontrol Properties pane, \uicontrol Id field, enter + \e bubble to be able to reference the image from other places. - \li In the code editor, add the following new properties to the image to - position the image at the center of the application window when the - application starts: - - \quotefromfile accelbubble/main.qml - \skipto Image - \printuntil bubble.width - - \li Set the x and y position of the image based on the new properties: - - \dots - \printuntil centerY - \skipto /^\}/ - \printuntil } + \li Select the \inlineimage qmldesigner-export-item-button.png + (\uicontrol Export) button in the navigator to export the + \e mainWindow and \e bubble as properties. \endlist - For an example, see \l{accelbubble/main.qml}{main.qml}. + We want to modify the properties of the bubble in ways that are not + supported by \QMLD, and therefore we create a custom QML type for it: + + \list 1 + + \li Select \uicontrol Edit to open MainForm.ui.qml in the code editor. + + \li Right-click \uicontrol Image and select \uicontrol Refactoring > + \uicontrol {Move Component into Separate File}. + + \li In the \uicontrol {Component name} field, enter \e Bubble and + select \uicontrol OK to create \e Bubble.qml. + + \endlist + + \QC creates a reference to Bubble.qml in MainForm.ui.qml. + + To check your code, you can compare MainForm.ui.qml with the + \l{accelbubble/MainForm.ui.qml}{MainForm.ui.qml} example file and Bubble.qml + with the \l{accelbubble/Bubble.qml}{Bubble.qml} example file. + + The UI is now ready and you can switch to editing the main.qml and + Bubble.qml files in the \uicontrol Edit mode, as described in the + following section. \section1 Moving the Bubble - Now that the visual elements are in place, let us move the bubble based on - Accelerometer sensor values. + The new project wizard adds boilerplate code to the main.qml file to create + menu items and push buttons. Modify the boilerplate code by removing + obsolete code and by adding new code. You removed the push buttons from the + UI Form, so you also need to remove the corresponding code from main.qml (or + the application cannot be built). + + In the code editor, edit Bubble.qml to add properties that we will use to + position the image: + + \quotefromfile accelbubble/Bubble.qml + \skipto Image + \printuntil } + + In the code editor, edit the main.qml to specify the application title, as + illustrated by the following code snippet: + + \quotefromfile accelbubble/main.qml + \skipto ApplicationWindow + \printuntil title + + Use the bubble properties to position the image: + + \printuntil bubbleCenter + + Then set the x and y position of the image based on the new properties: + + \printuntil centerY + \skipto /^\}/ + \printuntil } + + Then add code to move the bubble based on Accelerometer sensor values: \list 1 \li Add the following import statement to main.qml: \code - import QtSensors 5.0 + import QtSensors 5.5 \endcode \li Add the \l{Accelerometer} type with the necessary properties: @@ -183,7 +192,7 @@ \printuntil Math.atan(x \printuntil } - \li Add the following JavaScript code for \a onReadingChanged signal of + \li Add the following JavaScript code for \c onReadingChanged signal of Accelerometer type to make the bubble move when the Accelerometer values change: @@ -194,7 +203,7 @@ We want to ensure that the position of the bubble is always within the bounds of the screen. If the Accelerometer returns not a number (NaN), the value is ignored and the bubble position is not updated. - \li Add SmoothedAnimation behavior on the \a x and \a y properties of + \li Add SmoothedAnimation behavior on the \c x and \c y properties of the bubble to make its movement look smoother. \quotefromfile accelbubble/main.qml diff --git a/doc/src/qtquick/qtquick-buttons.qdoc b/doc/src/qtquick/qtquick-buttons.qdoc index c13afccfdf8..5bccf04df1a 100644 --- a/doc/src/qtquick/qtquick-buttons.qdoc +++ b/doc/src/qtquick/qtquick-buttons.qdoc @@ -30,41 +30,51 @@ \title Creating Buttons - To create a button component: + To create a button type: \list 1 - \li Select \uicontrol {File > New File or Project > Qt > QML File > Choose} - to create a QML file called Button.qml (for example). + \li Select \uicontrol File > \uicontrol {New File or Project} > + \uicontrol Qt > \uicontrol {QML File (Qt Quick 2)} > + \uicontrol Choose to create a QML file called Button.qml (for + example). - \note Components are listed in the \uicontrol Library only if the + \note Types are listed in the \uicontrol Library only if the filename begins with a capital letter. \li Click \uicontrol {Design} to edit the file in the visual editor. - \li In the \uicontrol Navigator, click \uicontrol Rectangle to set properties - for it. + \li In the \uicontrol Navigator, select \uicontrol Item and set the + width (\uicontrol W) and height (\uicontrol H) of the button in the + \uicontrol Properties pane. - \li In the \uicontrol Properties pane, modify the appearance of the button. + \li Drag and drop a \uicontrol Rectangle from the \uicontrol Library to + the item in the navigator. This creates a nested item where the + item is the parent of the rectangle. Items are positioned relative + to their parents. + + \li In the \uicontrol Properties pane, modify the appearance of the + rectangle: \list a - \li In the \uicontrol Size field, set the width (\uicontrol W) and height - (\uicontrol H) of the button. - \li In the \uicontrol Color field, select the button color. \li In the \uicontrol Radius field, use the slider to set the radius of the rectangle and produce rounded corners for the button. + \li Select \uicontrol {Layout}, and then select the + \inlineimage qmldesigner-anchor-fill-screen.png + (\uicontrol {Fill to Parent}) button to anchor the rectangle to + the item. + + \endlist - \li Drag and drop a \uicontrol {Text} item on top of the \uicontrol Rectangle. This - creates a nested item where \uicontrol Rectangle is the parent item - of \uicontrol Text. Items are positioned relative to their parents. + \li Drag and drop a \uicontrol {Text} type to the item in the navigator. \li In the \uicontrol Properties pane, edit the properties of the \uicontrol Text - item. + type. \list a @@ -77,10 +87,8 @@ \li In the \uicontrol Alignment field, select the center buttons to align the text to the center of the button. - \li Click \uicontrol {Layout}, and then click the - \inlineimage qmldesigner-anchor-fill-screen.png - (\uicontrol {Fill to Parent}) button to anchor the text to the whole - button area. + \li Select \uicontrol Layout > \uicontrol {Fill to Parent} + to anchor the text to the whole button area. \endlist @@ -110,13 +118,13 @@ \l{BorderImage} type to display an image, such as a PNG file, as a border and a background. - Use two Border Image items and suitable graphics to make it look like the - button is pushed down when it is clicked. One of the Border Image items + Use two border images and suitable graphics to make it look like the + button is pushed down when it is clicked. One of the border images is visible by default. You can specify that it is hidden and the other one becomes visible when the mouse is clicked. - Add a Mouse Area that covers the whole area and emits the clicked signal - (\c {parent.clicked()}) when it detects a mouse click. + Add a \l MouseArea type that covers the whole area and emits the clicked + signal (\c {item.clicked()}) when it detects a mouse click. You can add text to the button and set it up as a property. The text can then be initialized from the outside, making the button a reusable UI @@ -130,57 +138,54 @@ \list 1 - \li Select \uicontrol {File > New File or Project > Qt > QML File > Choose} - to create a QML file called Button.qml (for example). + \li Select \uicontrol File > \uicontrol {New File or Project} > + \uicontrol Qt > \uicontrol {QML File (Qt Quick 2)} > + \uicontrol Choose to create a QML file called Button.qml (for + example). - \li Double-click the file to open it in the code editor. - - \li Replace the \uicontrol Rectangle with an \uicontrol Item, as illustrated by the - following code snippet: - - \qml - Item { - - } - \endqml - - \li Specify properties and set expressions for the \uicontrol Item, as - illustrated by the following code snippet: - - \qml - Item { - property string text: "" - property int fontSize: 10 - - signal clicked - - width: 60 - height: 40 - } - \endqml - - You will point to the properties and expression later. + \note Types are listed in the \uicontrol Library only if the + filename begins with a capital letter. \li Click \uicontrol {Design} to edit the file in the visual editor. - \li Drag and drop two \uicontrol {Border Image} items from the \uicontrol Library - to the canvas. + \li In the \uicontrol Navigator, select \uicontrol Item and set the + width (\uicontrol W) and height (\uicontrol H) of the button in the + \uicontrol Properties pane. - \li Drag and drop a \uicontrol Text item to the canvas. + \li Select \uicontrol Connections > \uicontrol {Dynamic Properties} > + \uicontrol Add to add properties for the item: - \li Drag and drop a \uicontrol {Mouse Area} to the canvas. + \image qmldesigner-borderimage-properties.png "Dynamic properties" - \li In the \uicontrol Navigator, select \uicontrol border_image1 to specify - settings for it in the \uicontrol Properties pane: + \list a + + \li Double-click in the columns in the view to specify a \c text + property with the type \c string and an empty value. + + \li Specify another property, \c fontSize, with the type \c int + and the value \c 10. + + \endlist + + \li Drag and drop two \uicontrol {Border Image} types from the + \uicontrol Library to the item in the \uicontrol Navigator. + + \li Drag and drop a \uicontrol Text type to the item in the navigator. + + \li Drag and drop a \uicontrol {Mouse Area} to the item in the + navigator. + + \li In the navigator, select a border image to specify settings for it + in the \uicontrol Properties pane: \list a - \li Select \uicontrol {Set Binding} in the menu next to the - \uicontrol Visibility check box. + \li Select \uicontrol Connections > \uicontrol {Bindings} + > \uicontrol Add to hide the image when the mouse button is not + pressed down. Specify the \c visible property with the + \c !mouseArea source item and \c pressed source property. - \li Enter the following expression to specify that the image is - visible when the mouse is not pressed down: - \c {!mouse_area1.pressed}. + \image qmldesigner-borderimage-bindings.png "Border image bindings" \li In the \uicontrol Source field, select the image file for the button, for example button_up.png. @@ -192,41 +197,48 @@ \endlist - \li Select \uicontrol border_image2 to specify similar settings for it: + \li Select the other border image to specify similar settings for it: \list a - \li Set the following epression for \uicontrol Visibility, to specify that - the image is visible when the mouse is pressed down: - \c {mouse_area1.pressed}. + \li In \uicontrol Bindings, specify the \c visible property + with the \c mouseArea source item and \c pressed source + property to show the image when the mouse button is pressed + down. \li In the \uicontrol Source field, select the image file for the button when it is clicked, for example button_down.png. - \li Click \uicontrol {Layout}, and then click the - \uicontrol {Fill to Parent} + \li Select \uicontrol {Layout} > \uicontrol {Fill to Parent} button to anchor the border image to the \uicontrol Item. \endlist - \li Select \uicontrol text1 to specify font size and color, and text - scaling and rendering: + \li Select the text to specify font size and color, and text scaling + and rendering: \list a \li In the \uicontrol Color field, use the color picker to select the font color, or enter a value in the field. - \li In the \uicontrol Text field, select \uicontrol {Set Binding} and - enter a pointer to the \c {text} property that you specified - earlier: \c {parent.txt}. + \li In \uicontrol Bindings, create bindings to properties: - \li In the \uicontrol Size field, select \uicontrol {Pixels} to specify the font - size in pixels. By default, the size is specified in points. + \image qmldesigner-borderimage-bindings-text.png "Text bindings" - \li In the \uicontrol Size field, select \uicontrol {Set Expression} and enter a - pointer to the \c {fontSize} property that you specified - earlier. + \list + + \li Set the source property of the \c text property as + \c text and the source item as \c parent. + + \li Set the source property of the \c font.pixelSize as + \c fontSize and the source item as \c parent. + + \li Set the source property of \c scale as + \c {if (!mouseArea} and the source item as + \c {pressed) { 1 } else { 0.95 }}. + + \endlist \li Click \uicontrol {Layout}, and then click the \inlineimage qmldesigner-center-in.png "Anchor buttons" @@ -234,20 +246,25 @@ buttons to inherit the vertical and horizontal centering from the parent. - \li Click \uicontrol Advanced to specify scaling for the text in the - \uicontrol Scale field. - - \li Select \uicontrol {Set Binding} and enter the following expression: - \c {if (!mousearea1.pressed) { 1 } else { 0.95 }}. - - \note You can enter long and complicated expressions also in the - code editor. - \endlist - \li In the code editor, add to the \c MouseArea a pointer to the - \c clicked expression that you added earlier: - \c {onClicked: parent.clicked()}. + \li Select \uicontrol mouseArea in the navigator and then select + \uicontrol Connections > \uicontrol Add to set \c item.clicked() + as the value of the \c onClicked signal handler. + + \image qmldesigner-borderimage-connections.png "Item connections" + + \li In the code editor, specify the \c clicked signal for the Item: + + \qml + Item { + id: item + property string text: "" + property int fontSize: 10 + + signal clicked + } + \endqml \endlist diff --git a/doc/src/qtquick/qtquick-designer.qdoc b/doc/src/qtquick/qtquick-designer.qdoc index 7eb6b3cf0e9..6b8e5cb03ed 100644 --- a/doc/src/qtquick/qtquick-designer.qdoc +++ b/doc/src/qtquick/qtquick-designer.qdoc @@ -109,14 +109,20 @@ Click the \inlineimage qmldesigner-show-hide-icon.png icon to change the visibility of an item on the canvas. To change the - visibility of an item in the application, use the \uicontrol Visibility - check box or the \uicontrol Opacity field in the \uicontrol Properties pane. If you set - \uicontrol Opacity to 0, items are hidden, but you can still apply animation - to them. + visibility of an item in the application, select the \uicontrol Visibility + check box in the \uicontrol Properties pane or select \uicontrol Edit > + \uicontrol Visibility in the context menu. + + You can also set the \uicontrol Opacity field to 0 to hide items that you + want to apply animation to. As all properties, visibility and opacity are inherited from the parent item. To hide or show child items, edit the properties of the parent item. + To reset item size, position, or anchors, select context menu commands. To + change the source of an Image type, select \uicontrol {Change Source URL} in + the context menu. + To view lists of files or projects, instead, select \uicontrol {File System}, \uicontrol {Open Documents}, or \uicontrol Projects in the menu. To view several types of content at a time, split the sidebar by clicking @@ -132,7 +138,8 @@ stacking value are drawn in the order they are listed, from the last item up. - To change the stacking order of an item, right-click it on the canvas and + To change the stacking order of an item, right-click it in the navigator or + on the canvas and select \uicontrol {Stack (z)}. You can raise or lower the stack value of an item or move the item to the front or back of all its siblings. To remove the \c z property, select \uicontrol {Reset z Property}. diff --git a/doc/src/qtquick/qtquick-ui-forms.qdoc b/doc/src/qtquick/qtquick-ui-forms.qdoc index 1b4b3b07997..37e70a5a93a 100644 --- a/doc/src/qtquick/qtquick-ui-forms.qdoc +++ b/doc/src/qtquick/qtquick-ui-forms.qdoc @@ -103,4 +103,8 @@ You can also assign properties or define behavior or transitions. + To move from the \uicontrol Design mode directly to the implementation of a + type in the .qml file, right-click the type in the \uicontrol Navigator and + select \uicontrol {Go to Implementation} in the context menu. + */ diff --git a/scripts/deployqt.py b/scripts/deployqt.py index e6e7a2e437b..53220365120 100755 --- a/scripts/deployqt.py +++ b/scripts/deployqt.py @@ -73,7 +73,7 @@ def is_debug(fpath): if coredebug.search(fpath): return True output = subprocess.check_output(['dumpbin', '/imports', fpath]) - return coredebug.search(output) + return coredebug.search(output) != None def is_debug_build(install_dir): return is_debug(os.path.join(install_dir, 'bin', 'qtcreator.exe')) diff --git a/share/qtcreator/debugger/boosttypes.py b/share/qtcreator/debugger/boosttypes.py index 9c0c29158d9..cf0b4587b06 100644 --- a/share/qtcreator/debugger/boosttypes.py +++ b/share/qtcreator/debugger/boosttypes.py @@ -103,7 +103,11 @@ def qdump__boost__container__list(d, value): innerType = d.templateArgument(value.type, 0) offset = 2 * d.ptrSize() with Children(d, n): - p = r["root_"]["next_"] + try: + root = r["root_"] + except: + root = r["m_header"] + p = root["next_"] for i in xrange(n): d.putSubItem("%s" % i, d.createValue(d.pointerValue(p) + offset, innerType)) p = p["next_"] @@ -130,14 +134,41 @@ def qdump__boost__unordered__unordered_set(d, value): ptrSize = d.ptrSize() size = d.extractInt(base + 2 * ptrSize) d.putItemCount(size) + if d.isExpanded(): innerType = d.templateArgument(value.type, 0) bucketCount = d.extractInt(base + ptrSize) - offset = int((innerType.sizeof + ptrSize - 1) / ptrSize) * ptrSize - with Children(d, size, maxNumChild=10000): - afterBuckets = d.extractPointer(base + 5 * ptrSize) - afterBuckets += bucketCount * ptrSize - item = d.extractPointer(afterBuckets) - for j in d.childRange(): - d.putSubItem(j, d.createValue(item - offset, innerType)) - item = d.extractPointer(item) + #warn("A BUCKET COUNT: %s" % bucketCount) + #warn("X BUCKET COUNT: %s" % d.parseAndEvaluate("s1.table_.bucket_count_")) + try: + # boost 1.58 + table = value["table_"] + bucketsAddr = toInteger(table["buckets_"]) + #warn("A BUCKETS: 0x%x" % bucketsAddr) + #warn("X BUCKETS: %s" % d.parseAndEvaluate("s1.table_.buckets_")) + lastBucketAddr = bucketsAddr + bucketCount * ptrSize + #warn("A LAST BUCKET: 0x%x" % lastBucketAddr) + #warn("X LAST BUCKET: %s" % d.parseAndEvaluate("s1.table_.get_bucket(s1.table_.bucket_count_)")) + previousStartAddr = lastBucketAddr + #warn("A PREVIOUS START: 0x%x" % previousStartAddr) + #warn("X PREVIOUS START: %s" % d.parseAndEvaluate("s1.table_.get_previous_start()")) + item = d.extractPointer(previousStartAddr) + #warn("A KEY ADDR: 0x%x" % item) + #warn("X KEY ADDR: %s" % d.parseAndEvaluate("s1.table_.get_previous_start()->next_")) + item = d.extractPointer(previousStartAddr) + #warn("A VALUE: %x" % d.extractInt(item + ptrSize)) + #warn("X VALUE: %s" % d.parseAndEvaluate("*(int*)(s1.table_.get_previous_start()->next_ + 1)")) + with Children(d, size, maxNumChild=10000): + for j in d.childRange(): + d.putSubItem(j, d.createValue(item + 2 * ptrSize, innerType)) + item = d.extractPointer(item) + except: + # boost 1.48 + offset = int((innerType.sizeof + ptrSize - 1) / ptrSize) * ptrSize + with Children(d, size, maxNumChild=10000): + afterBuckets = d.extractPointer(base + 5 * ptrSize) + afterBuckets += bucketCount * ptrSize + item = d.extractPointer(afterBuckets) + for j in d.childRange(): + d.putSubItem(j, d.createValue(item - offset, innerType)) + item = d.extractPointer(item) diff --git a/share/qtcreator/debugger/dumper.py b/share/qtcreator/debugger/dumper.py index 03320cab10f..07fac849806 100644 --- a/share/qtcreator/debugger/dumper.py +++ b/share/qtcreator/debugger/dumper.py @@ -569,7 +569,9 @@ class DumperBase: elided, shown = self.computeLimit(size, limit) return elided, self.readMemory(data, shown) - def putCharArrayHelper(self, data, size, charSize, displayFormat = AutomaticFormat): + def putCharArrayHelper(self, data, size, charSize, + displayFormat = AutomaticFormat, + makeExpandable = True): bytelen = size * charSize elided, shown = self.computeLimit(bytelen, self.displayStringLimit) mem = self.readMemory(data, shown) @@ -595,6 +597,13 @@ class DumperBase: elided, shown = self.computeLimit(bytelen, 100000) self.putField("editvalue", self.readMemory(data, shown)) + if makeExpandable: + self.putNumChild(size) + if self.isExpanded(): + with Children(self): + for i in range(size): + self.putSubItem(size, data[i]) + def readMemory(self, addr, size): data = self.extractBlob(addr, size).toBytes() return self.hexencode(data) @@ -795,14 +804,9 @@ class DumperBase: raise RuntimeError("Check failed") def checkRef(self, ref): - try: - count = int(ref["atomic"]["_q_value"]) # Qt 5. - minimum = -1 - except: - count = int(ref["_q_value"]) # Qt 4. - minimum = 0 + count = self.extractInt(ref.address) # Assume there aren't a million references to any object. - self.check(count >= minimum) + self.check(count >= -1) self.check(count < 1000000) def readToFirstZero(self, p, tsize, maximum): @@ -1010,7 +1014,8 @@ class DumperBase: n = int(arrayByteSize / ts) if displayFormat != RawFormat and p: if innerTypeName == "char" or innerTypeName == "wchar_t": - self.putCharArrayHelper(p, n, ts, self.currentItemFormat()) + self.putCharArrayHelper(p, n, ts, self.currentItemFormat(), + makeExpandable = False) else: self.tryPutSimpleFormattedPointer(p, arrayType, innerTypeName, displayFormat, arrayByteSize) @@ -1410,8 +1415,7 @@ class DumperBase: addr += 1 return result - def listChildrenGenerator(self, addr, typeName): - innerType = self.lookupType(self.qtNamespace() + typeName) + def listChildrenGenerator(self, addr, innerType): base = self.extractPointer(addr) begin = self.extractInt(base + 8) end = self.extractInt(base + 12) @@ -1430,6 +1434,14 @@ class DumperBase: p = self.extractPointer(addr + i * stepSize) yield self.createValue(p, innerType) + def vectorChildrenGenerator(self, addr, innerType): + base = self.extractPointer(addr) + size = self.extractInt(base + 4) + data = base + self.extractPointer(base + 8 + self.ptrSize()) + innerSize = innerType.sizeof + for i in range(size): + yield self.createValue(data + i * innerSize, innerType) + # This is called is when a QObject derived class is expanded def putQObjectGuts(self, qobject, smo): @@ -1454,6 +1466,7 @@ class DumperBase: with SubItem(self, "[properties]"): propertyCount = 0 + usesVector = self.qtVersion() >= 0x50700 if self.isExpanded(): propertyNames = self.staticQObjectPropertyNames(smo) propertyCount = len(propertyNames) # Doesn't include dynamic properties. @@ -1465,8 +1478,13 @@ class DumperBase: # Dynamic properties. if extraData: - names = self.listChildrenGenerator(extraData + ptrSize, "QByteArray") - values = self.listChildrenGenerator(extraData + 2 * ptrSize, "QVariant") + byteArrayType = self.lookupQtType("QByteArray") + variantType = self.lookupQtType("QVariant") + names = self.listChildrenGenerator(extraData + ptrSize, byteArrayType) + if usesVector: + values = self.vectorChildrenGenerator(extraData + 2 * ptrSize, variantType) + else: + values = self.listChildrenGenerator(extraData + 2 * ptrSize, variantType) for (k, v) in zip(names, values): with SubItem(self, propertyCount): self.put('key="%s",' % self.encodeByteArray(k)) diff --git a/share/qtcreator/debugger/gdbbridge.py b/share/qtcreator/debugger/gdbbridge.py index 213cede8e59..fec5102627f 100644 --- a/share/qtcreator/debugger/gdbbridge.py +++ b/share/qtcreator/debugger/gdbbridge.py @@ -226,6 +226,11 @@ class Dumper(DumperBase): self.qtNamespaceToReport = None self.interpreterBreakpointResolvers = [] + # The guess does not need to be updated during a fetchVariables() + # as the result is fixed during that time (ignoring "active" + # dumpers causing loading of shared objects etc). + self.currentQtNamespaceGuess = None + def prepare(self, args): self.output = [] self.currentIName = "" @@ -238,11 +243,6 @@ class Dumper(DumperBase): self.currentType = ReportItem() self.currentAddress = None - # The guess does not need to be updated during a fetchVariables() - # as the result is fixed during that time (ignoring "active" - # dumpers causing loading of shared objects etc). - self.currentQtNamespaceGuess = None - self.resultVarName = args.get("resultvarname", "") self.expandedINames = set(args.get("expanded", [])) self.stringCutOff = int(args.get("stringcutoff", 10000)) @@ -1159,6 +1159,38 @@ class Dumper(DumperBase): # return mem.tobytes() return mem + def createSpecialBreakpoints(self, args): + self.specialBreakpoints = [] + def newSpecial(spec): + class SpecialBreakpoint(gdb.Breakpoint): + def __init__(self, spec): + super(SpecialBreakpoint, self).\ + __init__(spec, gdb.BP_BREAKPOINT, internal=True) + self.spec = spec + + def stop(self): + print("Breakpoint on '%s' hit." % self.spec) + return True + return SpecialBreakpoint(spec) + + # FIXME: ns is accessed too early. gdb.Breakpoint() has no + # 'rbreak' replacement, and breakpoints created with + # 'gdb.execute("rbreak...") cannot be made invisible. + # So let's ignore the existing of namespaced builds for this + # fringe feature here for now. + ns = self.qtNamespace() + if args.get('breakonabort', 0): + self.specialBreakpoints.append(newSpecial("abort")) + + if args.get('breakonwarning', 0): + self.specialBreakpoints.append(newSpecial(ns + "qWarning")) + self.specialBreakpoints.append(newSpecial(ns + "QMessageLogger::warning")) + + if args.get('breakonfatal', 0): + self.specialBreakpoints.append(newSpecial(ns + "qFatal")) + self.specialBreakpoints.append(newSpecial(ns + "QMessageLogger::fatal")) + + def putFields(self, value, dumpBase = True): fields = value.type.fields() if self.sortStructMembers: diff --git a/share/qtcreator/debugger/lldbbridge.py b/share/qtcreator/debugger/lldbbridge.py index d38e9d1f5da..e49d71730fd 100644 --- a/share/qtcreator/debugger/lldbbridge.py +++ b/share/qtcreator/debugger/lldbbridge.py @@ -1115,21 +1115,41 @@ class Dumper(DumperBase): self.putItem(child) return - n = value.GetNumChildren() - m = value.GetType().GetNumberOfDirectBaseClasses() - if n > 10000: - n = 10000 - # seems to happen in the 'inheritance' autotest - if m > n: - m = n - for i in xrange(m): - child = value.GetChildAtIndex(i) - with UnnamedSubItem(self, "@%d" % (i + 1)): - self.put('iname="%s",' % self.currentIName) - self.put('name="[%s]",' % child.name) - self.putItem(child) + memberBase = 0 # Start of members. - children = [value.GetChildAtIndex(i) for i in xrange(m, n)] + class ChildItem: + def __init__(self, name, value): + self.name = name + self.value = value + + baseObjects = [] + for i in xrange(value.GetType().GetNumberOfDirectBaseClasses()): + baseClass = value.GetType().GetDirectBaseClassAtIndex(i).GetType() + baseChildCount = baseClass.GetNumberOfFields() \ + + baseClass.GetNumberOfDirectBaseClasses() \ + + baseClass.GetNumberOfVirtualBaseClasses() + if baseChildCount: + memberBase += 1 + baseObjects.append(ChildItem(baseClass.GetName(), value.GetChildAtIndex(i))) + else: + # This base object is empty, but exists and will *not* be reported + # by value.GetChildCount(). So manually report the empty base class. + baseObject = value.Cast(baseClass) + baseObjects.append(ChildItem(baseClass.GetName(), baseObject)) + + if self.sortStructMembers: + baseObjects.sort(key = lambda baseObject: str(baseObject.name)) + for i in xrange(len(baseObjects)): + baseObject = baseObjects[i] + with UnnamedSubItem(self, "@%d" % (i + 1)): + self.put('iname="%s",' % self.currentIName) + self.put('name="[%s]",' % baseObject.name) + self.putItem(baseObject.value) + + memberCount = value.GetNumChildren() + if memberCount > 10000: + memberCount = 10000 + children = [value.GetChildAtIndex(memberBase + i) for i in xrange(memberCount)] if self.sortStructMembers: children.sort(key = lambda child: str(child.GetName())) for child in children: @@ -1747,14 +1767,9 @@ class Dumper(DumperBase): # Used in dumper auto test. class Tester(Dumper): - def __init__(self, binary, expandedINames): + def __init__(self, binary, args): Dumper.__init__(self) lldb.theDumper = self - - self.expandedINames = set(expandedINames) - self.passExceptions = True - self.sortStructMembers = True - self.loadDumpers({'token': 1}) error = lldb.SBError() self.target = self.debugger.CreateTarget(binary, None, None, True, error) @@ -1763,14 +1778,14 @@ class Tester(Dumper): warn("ERROR: %s" % error) return - s = threading.Thread(target=self.testLoop, args=[]) + s = threading.Thread(target=self.testLoop, args=(args,)) s.start() s.join(30) def reportDumpers(self, msg): pass - def testLoop(self): + def testLoop(self, args): # Disable intermediate reporting. savedReport = self.report self.report = lambda stuff: 0 @@ -1813,7 +1828,7 @@ class Tester(Dumper): if line != 0: self.report = savedReport self.process.SetSelectedThread(stoppedThread) - self.fetchVariables({'token':2, 'fancy':1}) + self.fetchVariables(args) #self.describeLocation(frame) self.report("@NS@%s@" % self.qtNamespace()) #self.report("ENV=%s" % os.environ.items()) diff --git a/share/qtcreator/debugger/qttypes.py b/share/qtcreator/debugger/qttypes.py index 71e9a276b28..6f3685375a4 100644 --- a/share/qtcreator/debugger/qttypes.py +++ b/share/qtcreator/debugger/qttypes.py @@ -33,24 +33,24 @@ from dumper import * def qdump__QAtomicInt(d, value): - d.putValue(int(value["_q_value"])) + d.putValue(d.extractInt(value.address)) d.putNumChild(0) def qdump__QBasicAtomicInt(d, value): - d.putValue(int(value["_q_value"])) + d.putValue(d.extractInt(value.address)) d.putNumChild(0) def qdump__QAtomicPointer(d, value): d.putType(value.type) - q = value["_q_value"] + q = d.extractPointer(value.address) p = toInteger(q) d.putValue("@0x%x" % p) d.putNumChild(1 if p else 0) if d.isExpanded(): with Children(d): - d.putSubItem("_q_value", q.dereference()) + d.putSubItem("[pointee]", q.dereference()) def qform__QByteArray(): return [Latin1StringFormat, SeparateLatin1StringFormat, @@ -422,7 +422,12 @@ def qdump__QFile(d, value): # 9fc0965 and a373ffcd change the layout of the private structure qtVersion = d.qtVersion() is32bit = d.is32bit() - if qtVersion >= 0x050500: + if qtVersion >= 0x050600: + if d.isWindowsTarget(): + offset = 164 if is32bit else 248 + else: + offset = 168 if is32bit else 248 + elif qtVersion >= 0x050500: if d.isWindowsTarget(): offset = 164 if is32bit else 248 else: @@ -697,38 +702,71 @@ def qdump__QHostAddress(d, value): # bool isParsed (2*ptrSize + 24) privAddress = d.extractPointer(value) - isQt5 = d.qtVersion() >= 0x050000 - sizeofQString = d.ptrSize() - ipStringAddress = privAddress + (0 if isQt5 else 24) - isParsedAddress = privAddress + 24 + 2 * sizeofQString - # value.d.d->ipString - ipString = d.encodeString(ipStringAddress, limit=100) - if d.extractByte(isParsedAddress) and len(ipString) > 0: - d.putValue(ipString, Hex4EncodedLittleEndian) - else: - # value.d.d->protocol: - # QAbstractSocket::IPv4Protocol = 0 - # QAbstractSocket::IPv6Protocol = 1 - protoAddress = privAddress + 20 + (2 * sizeofQString if isQt5 else 0); - proto = d.extractInt(protoAddress) - if proto == 1: - # value.d.d->a6 - a6Offset = 4 + (2 * sizeofQString if isQt5 else 0) - data = d.readMemory(privAddress + a6Offset, 16) - address = ':'.join("%x" % int(data[i:i+4], 16) for i in xrange(0, 32, 4)) - scopeId = privAddress + sizeofQString + (0 if isQt5 else 24) - scopeId = d.encodeString(scopeId, limit=100) - d.putValue("%s%%%s" % (address, scopeId), IPv6AddressAndHexScopeId) - elif proto == 0: - # value.d.d->a - a = d.extractInt(privAddress + (2 * sizeofQString if isQt5 else 0)) - a, n4 = divmod(a, 256) - a, n3 = divmod(a, 256) - a, n2 = divmod(a, 256) - a, n1 = divmod(a, 256) - d.putValue("%d.%d.%d.%d" % (n1, n2, n3, n4)); + if d.qtVersion() >= 0x050700: + sizeofQString = d.ptrSize() + ipStringAddress = privAddress + a6Address = privAddress + 2 * sizeofQString + d.ptrSize() # Include padding + protoAddress = a6Address + 16 + isParsedAddress = protoAddress + 4 + # value.d.d->ipString + ipString = d.encodeString(ipStringAddress, limit=100) + if d.extractByte(isParsedAddress) and len(ipString) > 0: + d.putValue(ipString, Hex4EncodedLittleEndian) else: - d.putValue("") + # value.d.d->protocol: + # QAbstractSocket::IPv4Protocol = 0 + # QAbstractSocket::IPv6Protocol = 1 + proto = d.extractInt(protoAddress) + if proto == 1: + # value.d.d->a6 + data = d.readMemory(a6Address, 16) + address = ':'.join("%x" % int(data[i:i+4], 16) for i in xrange(0, 32, 4)) + scopeIdAddress = ipStringAddress + sizeofQString + scopeId = d.encodeString(scopeIdAddress, limit=100) + d.putValue("%s%%%s" % (address, scopeId), IPv6AddressAndHexScopeId) + elif proto == 0: + # value.d.d->a + a = d.extractInt(privAddress + 2 * sizeofQString) + a, n4 = divmod(a, 256) + a, n3 = divmod(a, 256) + a, n2 = divmod(a, 256) + a, n1 = divmod(a, 256) + d.putValue("%d.%d.%d.%d" % (n1, n2, n3, n4)); + else: + d.putValue("") + else: + isQt5 = d.qtVersion() >= 0x050000 + sizeofQString = d.ptrSize() + ipStringAddress = privAddress + (0 if isQt5 else 24) + isParsedAddress = privAddress + 24 + 2 * sizeofQString + # value.d.d->ipString + ipString = d.encodeString(ipStringAddress, limit=100) + if d.extractByte(isParsedAddress) and len(ipString) > 0: + d.putValue(ipString, Hex4EncodedLittleEndian) + else: + # value.d.d->protocol: + # QAbstractSocket::IPv4Protocol = 0 + # QAbstractSocket::IPv6Protocol = 1 + protoAddress = privAddress + 20 + (2 * sizeofQString if isQt5 else 0); + proto = d.extractInt(protoAddress) + if proto == 1: + # value.d.d->a6 + a6Offset = 4 + (2 * sizeofQString if isQt5 else 0) + data = d.readMemory(privAddress + a6Offset, 16) + address = ':'.join("%x" % int(data[i:i+4], 16) for i in xrange(0, 32, 4)) + scopeId = privAddress + sizeofQString + (0 if isQt5 else 24) + scopeId = d.encodeString(scopeId, limit=100) + d.putValue("%s%%%s" % (address, scopeId), IPv6AddressAndHexScopeId) + elif proto == 0: + # value.d.d->a + a = d.extractInt(privAddress + (2 * sizeofQString if isQt5 else 0)) + a, n4 = divmod(a, 256) + a, n3 = divmod(a, 256) + a, n2 = divmod(a, 256) + a, n1 = divmod(a, 256) + d.putValue("%d.%d.%d.%d" % (n1, n2, n3, n4)); + else: + d.putValue("") d.putPlainChildren(value["d"]["d"].dereference()) @@ -1656,7 +1694,7 @@ def qdump__QSet(d, value): def qdump__QSharedData(d, value): - d.putValue("ref: %s" % value["ref"]["_q_value"]) + d.putValue("ref: %s" % d.extractInt(value["ref"].address)) d.putNumChild(0) @@ -2213,8 +2251,8 @@ def qdump__QWeakPointer(d, value): d.putValue("") d.putNumChild(0) return - weakref = int(d_ptr["weakref"]["_q_value"]) - strongref = int(d_ptr["strongref"]["_q_value"]) + weakref = d.extractInt(d_ptr["weakref"].address) + strongref = d.extractInt(d_ptr["strongref"].address) d.check(strongref >= -1) d.check(strongref <= weakref) d.check(weakref <= 10*1000*1000) diff --git a/share/qtcreator/debugger/stdtypes.py b/share/qtcreator/debugger/stdtypes.py index 60c271ebcd6..2cde82e19a1 100644 --- a/share/qtcreator/debugger/stdtypes.py +++ b/share/qtcreator/debugger/stdtypes.py @@ -172,6 +172,9 @@ def qdump__std__list__QNX(d, value): def qdump__std____debug__list(d, value): qdump__std__list(d, value) +def qdump__std____cxx11__list(d, value): + qdump__std__list(d, value) + def qform__std__map(): return mapForms() @@ -289,7 +292,12 @@ def stdTreeIteratorHelper(d, value): nodeTypeName = str(value.type).replace("_Rb_tree_iterator", "_Rb_tree_node", 1) nodeTypeName = nodeTypeName.replace("_Rb_tree_const_iterator", "_Rb_tree_node", 1) nodeType = d.lookupType(nodeTypeName + '*') - data = node.cast(nodeType).dereference()["_M_value_field"] + nnode = node.cast(nodeType).dereference() + try: + data = nnode["_M_value_field"] + except: # GCC 5.x, C++11. + data = nnode["_M_storage"] # __gnu_cxx::__aligned_membuf + data = data.cast(d.templateArgument(data.type, 0)) first = d.childWithName(data, "first") if first: d.putSubItem("first", first) @@ -463,7 +471,7 @@ def qdump__std____1__wstring(d, value): size = firstByte / 2 data = base + 4 d.putCharArrayHelper(data, size, 4) - d.putType("std::xxwstring") + d.putType("std::wstring") def qdump__std__shared_ptr(d, value): @@ -796,13 +804,29 @@ def qform__std__wstring(): def qdump__std__wstring(d, value): charSize = d.lookupType('wchar_t').sizeof - # HACK: Shift format by 4 to account for latin1 and utf8 qdump__std__stringHelper1(d, value, charSize, d.currentItemFormat()) def qdump__std__basic_string(d, value): innerType = d.templateArgument(value.type, 0) qdump__std__stringHelper1(d, value, innerType.sizeof, d.currentItemFormat()) +def qdump__std____cxx11__basic_string(d, value): + innerType = d.templateArgument(value.type, 0) + data = value["_M_dataplus"]["_M_p"] + size = int(value["_M_string_length"]) + d.check(0 <= size) #and size <= alloc and alloc <= 100*1000*1000) + d.putCharArrayHelper(data, size, innerType.sizeof, d.currentItemFormat()) + +def qform__std____cxx11__string(d, value): + qdump__std____cxx11__basic_string(d, value) + +# Needed only to trigger the form report above. +def qform__std____cxx11__string(): + return qform__std__string() + +def qform__std____cxx11__wstring(): + return qform__std__wstring() + def qdump__std____1__basic_string(d, value): innerType = str(d.templateArgument(value.type, 0)) if innerType == "char": diff --git a/share/qtcreator/translations/qtcreator_ru.ts b/share/qtcreator/translations/qtcreator_ru.ts index 1eab1e6d929..51559890faf 100644 --- a/share/qtcreator/translations/qtcreator_ru.ts +++ b/share/qtcreator/translations/qtcreator_ru.ts @@ -88,22 +88,42 @@ Debug Отладка + + Profile + Профилирование + Release Выпуск - Tool - Инструмент + in Debug mode + она создана для режима отладки + + + in Profile mode + она создана для режима профилирования + + + in Release mode + она создана для режима выпуска + + + with debug symbols (Debug or Profile mode) + ей требуются отладочные символы (отладка или профилирование) + + + on optimized code (Profile or Release mode) + ей требуется оптимизированный код (профилирование или выпуск) + + + <html><head/><body><p>You are trying to run the tool "%1" on an application in %2 mode. The tool is designed to be used %3.</p><p>Run-time characteristics differ significantly between optimized and non-optimized binaries. Analytical findings for one mode may or may not be relevant for the other.</p><p>Running tools that need debug symbols on binaries that don't provide any may lead to missing function names or otherwise insufficient output.</p><p>Do you want to continue and run the tool in %2 mode?</p></body></html> + <html><head/><body><p>Вы пытаетесь запустить утилиту «%1» для приложения в режиме %2, а %3.</p><p>Характер работы приложения сильно зависит от оптимизации. Выводы, сделанные для одного режима, могут быть неверны для другого.</p><p>Запуск утилиты требующей отладочные символы для программ их не имеющих приведёт к проблемам определения имён функций или некорректному выводу информации.</p><p>Запустить утилиту в режиме %2?</p></body></html> Run %1 in %2 Mode? Выполнить %1 в режиме %2? - - <html><head/><body><p>You are trying to run the tool "%1" on an application in %2 mode. The tool is designed to be used in %3 mode.</p><p>Debug and Release mode run-time characteristics differ significantly, analytical findings for one mode may or may not be relevant for the other.</p><p>Do you want to continue and run the tool in %2 mode?</p></body></html> - <html><head/><body><p>Попытка выполнить утилиту «%1» для приложения в режиме %2. Утилита разработана для использования в режиме %3.</p><p>Работа в режимах отладки и выпуска значительно отличается, поэтому проблемы, найденные для одного из них, могут отсутствовать у другого.</p><p>Выполнить запуск утилиты в режиме %2?</p></body></html> - Analyze Анализ @@ -231,8 +251,8 @@ Сборка Android APK - Warning: Signing a debug package. - Предупреждение: Подписывание отладочного пакета. + Warning: Signing a debug or profile package. + Предупреждение: Подписывание отладочного или профилируемого пакета. Error @@ -1375,6 +1395,33 @@ Deploying local Qt libraries is incompatible with Android 5. Подходящая тема «%1» не обнаружена + + ApplicationWindowSpecifics + + Window + Окно + + + Title + Заголовок + + + Size + Размер + + + Color + Цвет + + + Visible + Видимость + + + Opacity + Непрозрачность + + AutotoolsProjectManager::Internal::AutogenStep @@ -1560,10 +1607,6 @@ Deploying local Qt libraries is incompatible with Android 5. BareMetal - - BareMetal - BareMetal - Enter GDB commands to reset the board and to write the nonvolatile memory. Введите команды GDB для сброса платы и записи в энергонезависимую память. @@ -1572,6 +1615,10 @@ Deploying local Qt libraries is incompatible with Android 5. Enter GDB commands to reset the hardware. The MCU should be halted after these commands. Введите команды GDB для аппаратного сброса. После этих команд процессор должен быть остановлен. + + Bare Metal + Голое железо + BareMetal::GdbServerProvider @@ -2018,8 +2065,8 @@ Deploying local Qt libraries is incompatible with Android 5. Некорректный профиль Qt. - Requires Qt 4.8.0 or newer. - Требуется Qt версии 4.8.0 или выше. + Requires Qt 5.0.0 or newer. + Требуется Qt версии 5.0.0 или выше. Requires Qt 5.3.0 or newer. @@ -2029,10 +2076,6 @@ Deploying local Qt libraries is incompatible with Android 5. This Qt Version does not contain Qt Quick Compiler. Этот профиль Qt не содержит компилятора Qt Quick. - - Building Debugging Helpers - Сборка помощников отладчика - Bazaar::Internal::BazaarCommitPanel @@ -3213,10 +3256,6 @@ For example, "Revision: 15" will leave the branch at revision 15.Show zoom slider. Показать ползунок масштабирования. - - Filter Categories - Признаки отбора - Select range. Выбрать диапазон. @@ -3237,6 +3276,13 @@ For example, "Revision: 15" will leave the branch at revision 15.Не настроено + + CMakeProjectManager::CMakeProject + + Internal Error: No build configuration found in settings file. + Внутренняя ошибка: Не удалось обнаружить конфигурацию сборки в файле настроек. + + CMakeProjectManager::CMakeSettingsPage @@ -3290,6 +3336,22 @@ For example, "Revision: 15" will leave the branch at revision 15.Build Сборка + + Debug + Отладка + + + Release + Выпуск + + + Minimum Size Release + Выпуск минимального размера + + + Release with Debug Information + Выпуск с отладочной информацией + CMakeProjectManager::Internal::CMakeBuildSettingsWidget @@ -3402,41 +3464,33 @@ For example, "Revision: 15" will leave the branch at revision 15.Запустить CMake - The directory %1 already contains a cbp file, which is recent enough. You can pass special arguments and rerun CMake. Or simply finish the wizard directly. - Каталог %1 уже содержит достаточно свежий файл cbp. Вы можете передать дополнительные параметры и перезапустить CMake. Либо просто завершить работу с мастером. + The build directory "%1" for build configuration "%2" for target "%3" contains an outdated .cbp file. Qt Creator needs to update this file by running CMake. You can add command line arguments below. Note that CMake remembers command line arguments from the previous runs. + Каталог сборки «%1» конфигурации «%2» для цели «%3» содержит устаревший файл *.cbp. Qt Creator должен его обновить путём запуска CMake. Можно передать дополнительные параметры командной строки ниже. Следует иметь в виду, что CMake запоминает параметры командной строки каждого запуска. - The directory %1 does not contain a cbp file. Qt Creator needs to create this file by running CMake. Some projects require command line arguments to the initial CMake call. - Каталог %1 не содержит файла cbp. Qt Creator создаст этот файл путём запуска CMake. Некоторые проекты требуют указания дополнительных параметров командной строки для первого запуска CMake. - - - The directory %1 contains an outdated .cbp file. Qt Creator needs to update this file by running CMake. If you want to add additional command line arguments, add them below. Note that CMake remembers command line arguments from the previous runs. - Каталог %1 содержит устаревший файл cbp. Qt Creator обновит этот файл путём запуска CMake. Если требуется указать дополнительные параметры командной строки, то можно добавить их ниже. Следует иметь в виду, что CMake запоминает параметры командной строки предыдущего запуска. - - - The directory %1 specified in a build-configuration, does not contain a cbp file. Qt Creator needs to recreate this file, by running CMake. Some projects require command line arguments to the initial CMake call. Note that CMake remembers command line arguments from the previous runs. - Указанный в настройках сборки каталог %1 не содержит файла cbp. Qt Creator создаст этот файл путём запуска CMake. Некоторые проекты требуют указания дополнительных параметров командной строки для первого запуска CMake. Следует иметь в виду, что CMake запоминает параметры командной строки предыдущего запуска. + The directory "%1" specified in build configuration "%2", for target "%3" does not contain a .cbp file. Qt Creator needs to recreate this file by running CMake. Some projects require command line arguments to the initial CMake call. Note that CMake remembers command line arguments from the previous runs. + Каталог сборки «%1» конфигурации «%2» цели «%3» не содержит файл *.cbp. Qt Creator должен его создать путём запуска CMake. Некоторым проектам требуется передать параметры командной строки при первом запуске CMake. Следует иметь в виду, что CMake запоминает параметры командной строки каждого запуска. Qt Creator needs to run CMake in the new build directory. Some projects require command line arguments to the initial CMake call. Qt Creator должен запустить CMake в новом каталоге сборки. Некоторые проекты требуют указания дополнительных параметров командной строки при первом запуске CMake. - Refreshing cbp file in %1. - Обновление файла cbp в %1. + Refreshing the .cbp file in "%1" for build configuration "%2" for target "%3". + Обновление файла *.cbp в «%1» конфигурации «%2» для цели «%3». - The cached generator %1 is incompatible with the configured kits. - Закэшированный генератор %1 несовместим с настроенными комплектами. + Selected kit has no valid CMake executable specified. + У выбранного комплекта не задана программа CMake. + + + Open project with errors. + Открывать проекты с ошибками. No generator selected. Генератор не выбран. - - Selected Kit has no valid CMake executable specified. - У выбранного комплекта не задана программа CMake. - CMake exited with errors. Please check CMake output. CMake завершился с ошибкой. Проверьте сообщения CMake. @@ -3484,7 +3538,7 @@ For example, "Revision: 15" will leave the branch at revision 15.Сделать по умолчанию - Set as the default CMake Tool to use when creating a new Kit, or no value is set. + Set as the default CMake Tool to use when creating a new kit or when no value is set. Задание утилиты CMake по умолчанию, используемой при создании новых комплектов. @@ -4210,10 +4264,6 @@ For example, "Revision: 15" will leave the branch at revision 15.Diff Сравнение - - &Graphical (Single file only) - &Графическое (только один файл) - &External &Внешнее @@ -4278,7 +4328,11 @@ For example, "Revision: 15" will leave the branch at revision 15.Фиксировать и получать файлы без комментариев (-nc/omment). - Do &not prompt for comment during check out or check in + &Graphical (single file only) + &Графическое (только один файл) + + + Do &not prompt for comment during checkout or check-in &Не спрашивать комментарии при получении и фиксации @@ -4293,7 +4347,7 @@ For example, "Revision: 15" will leave the branch at revision 15.При использовании внешней программы сравнения утилита «diff» должна быть доступна. - DiffUtils is available for free download athttp://gnuwin32.sourceforge.net/packages/diffutils.htm.Extract it to a directory in your PATH. + DiffUtils is available for free download at http://gnuwin32.sourceforge.net/packages/diffutils.htm. Extract it to a directory in your PATH. DiffUtils доступна для свободной загрузки отсюда: http://gnuwin32.sourceforge.net/packages/diffutils.htm. Распакуйте архив в любой каталог, прописанный в переменной среды PATH. @@ -4638,6 +4692,13 @@ p, li { white-space: pre-wrap; } Прозрачность + + ColorToolAction + + Edit Color + Изменить цвет + + ColumnSpecifics @@ -5384,9 +5445,13 @@ Continue? Core::Internal::CurrentDocumentFind - - %1 occurrences replaced. - Произведено %1 замен(а). + + %n occurrences replaced. + + Выполнена %n замена. + Выполнено %n замены. + Выполнено %n замен. + @@ -6176,25 +6241,13 @@ Do you want to kill it? Core::Internal::GeneralSettings - - Terminal: - Терминал: - - - ? - ? - - - General - Основные - <System Language> <Системный> - Command used for reverting diff chunks. - Команда, используемая для отката фрагментов diff. + Interface + Интерфейс Restart required @@ -6204,18 +6257,6 @@ Do you want to kill it? The language change will take effect after a restart of Qt Creator. Изменение языка вступит в силу после перезапуска Qt Creator. - - Variables - Переменные - - - When files are externally modified: - Когда файлы изменены извне: - - - External file browser: - Обозреватель файлов: - User Interface Интерфейс пользователя @@ -6228,83 +6269,28 @@ Do you want to kill it? Language: Язык: - - System - Система - - - Always Ask - Всегда спрашивать - - - Reload All Unchanged Editors - Перезагружать неизменённые - - - Ignore Modifications - Игнорировать изменения - Reset Сбросить - - Auto-save modified files - Автосохранение изменённых файлов - - - Interval: - Периодичность: - - - min - unit for minutes - мин - Reset to default. Color Сбросить в исходное состояние. - - Reset to default. - Terminal - Сбросить в исходное состояние. - - - Reset to default. - File Browser - Сбросить в исходное состояние. - Reset Warnings Button text Вернуть предупреждения - - Automatically creates temporary copies of modified files. If Qt Creator is restarted after a crash or power failure, it asks whether to recover the auto-saved content. - Автоматически создавать копии изменённых файлов. Если работа Qt Creator завершилась в результате краха или сбоя питания, то при перезапуске он предложит восстановить их содержимое. - Re-enable warnings that were suppressed by selecting "Do Not Show Again" (for example, missing highlighter). Вернуть предупреждения, отключённые кнопкой «Больше не показывать» (например, об отсутствии подсветки). - - Patch command: - Команда patch: - Theme: Тема: - - Warn before opening text files greater than - Предупреждать при открытии файлов больше - - - MB - МБ - Core::Internal::Locator @@ -6322,7 +6308,7 @@ Do you want to kill it? <html><body style="color:#909090; font-size:14px"><div align='center'><div style="font-size:20px">Open a document</div><table><tr><td><hr/><div style="margin-top: 5px">&bull; File > Open File or Project (%1)</div><div style="margin-top: 5px">&bull; File > Recent Files</div><div style="margin-top: 5px">&bull; Tools > Locate (%2) and</div><div style="margin-left: 1em">- type to open file from any open project</div>%4%5<div style="margin-left: 1em">- type <code>%3&lt;space&gt;&lt;filename&gt;</code> to open file from file system</div><div style="margin-left: 1em">- select one of the other filters for jumping to a location</div><div style="margin-top: 5px">&bull; Drag and drop files here</div></td></tr></table></div></body></html> - <html><body style="color:#909090; font-size:14px"><div align='center'><div style="font-size:20px">Открыть документ</div><table><tr><td><hr/><div style="margin-top: 5px">&bull; Файл > Открыть файл или проект (%1)</div><div style="margin-top: 5px">&bull; Файл > Недавние файлы</div><div style="margin-top: 5px">&bull; Инструменты > Найти (%2) и</div><div style="margin-left: 1em">- введите для открытия файла любого открытого проекта</div>%4%5<div style="margin-left: 1em">- введите <code>%3&lt;space&gt;&lt;имяфайла&gt;</code> для открытия любого файла с диска</div><div style="margin-left: 1em">- выберите любой другой фильтр для перехода</div><div style="margin-top: 5px">&bull; Перетаскивайте файлы тут</div></td></tr></table></div></body></html> + <html><body style="color:#909090; font-size:14px"><div align='center'><div style="font-size:20px">Открыть документ</div><table><tr><td><hr/><div style="margin-top: 5px">&bull; Файл > Открыть файл или проект (%1)</div><div style="margin-top: 5px">&bull; Файл > Недавние файлы</div><div style="margin-top: 5px">&bull; Инструменты > Найти (%2) и</div><div style="margin-left: 1em">- введите для открытия файла любого открытого проекта</div>%4%5<div style="margin-left: 1em">- введите <code>%3&lt;space&gt;&lt;имяфайла&gt;</code> для открытия любого файла с диска</div><div style="margin-left: 1em">- выберите любой другой фильтр для перехода</div><div style="margin-top: 5px">&bull; Перетащите файлы сюда</div></td></tr></table></div></body></html> <div style="margin-left: 1em">- type <code>%1&lt;space&gt;&lt;pattern&gt;</code> to jump to a class definition</div> @@ -7217,6 +7203,117 @@ Do you want to kill it? Не удалось открыть ссылку %1. + + Core::Internal::SystemSettings + + System + Системное + + + Terminal: + Терминал: + + + Warn before opening text files greater than + Предупреждать при открытии файлов больше + + + MB + МБ + + + Automatically creates temporary copies of modified files. If Qt Creator is restarted after a crash or power failure, it asks whether to recover the auto-saved content. + Автоматически создавать копии изменённых файлов. Если работа Qt Creator завершилась в результате краха или сбоя питания, то при перезапуске он предложит восстановить их содержимое. + + + Auto-save modified files + Автосохранение изменённых файлов + + + Interval: + Периодичность: + + + min + unit for minutes + мин + + + When files are externally modified: + Когда файлы изменены извне: + + + Always Ask + Всегда спрашивать + + + Reload All Unchanged Editors + Перезагружать неизменённые + + + Ignore Modifications + Игнорировать изменения + + + Patch command: + Команда patch: + + + ? + ? + + + Reset to default. + File Browser + Сбросить в исходное состояние. + + + Reset + Сброс + + + External file browser: + Обозреватель файлов: + + + Reset to default. + Terminal + Сбросить в исходное состояние. + + + <html>Influences how file names are matched to decide if they are the same. + <html>Определяет, как сравнивать похожие именя файлов. + + + File system case sensitivity: + Будет: "При стравнении имён файлов: учитывать регистр" + При сравнении имён файлов: + + + Command used for reverting diff chunks. + Команда, используемая для отката фрагментов diff. + + + Case Sensitive (Default) + Учитывать регистр (по умолчанию) + + + Case Sensitive + Учитывать регистр + + + Case Insensitive (Default) + Не учитывать регистр (по умолчанию) + + + Case Insensitive + Не учитывать регистр + + + Variables + Переменные + + Core::Internal::ThemeEditor::ThemeEditorWidget @@ -7350,17 +7447,17 @@ Do you want to kill it? О Qt Creator - Built on %1 %2<br/> - Собрано %1 в %2<br/> - - - <h3>%1</h3>%2<br/><br/>%3<br/>%4<br/>Copyright 2008-%5 %6. All rights reserved.<br/><br/>The program is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.<br/> - <h3>%1</h3>%2<br/><br/>%3<br/>%4<br/>© 2008-%5 %6. Все права защищены.<br/><br/>The program is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.<br/> - - - From revision %1<br/> + <br/>From revision %1<br/> This gets conditionally inserted as argument %8 into the description string. - Ревизия %1<br/> + <br/>Ревизия %1<br/> + + + <br/>Built on %1 %2<br/> + <br/>Собрано %1 в %2<br/> + + + <h3>%1</h3>%2<br/>%3%4%5<br/>Copyright 2008-%6 %7. All rights reserved.<br/><br/>The program is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.<br/> + <h3>%1</h3>%2<br/>%3%4%5<br/>© 2008-%6 %7. Все права защищены.<br/><br/>The program is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.<br/> @@ -7385,10 +7482,8 @@ Do you want to kill it? Не удалось преобразовать результат «%1» в строку. - Evaluate simple Javascript statements. -The statements may not contain '{' nor '}' characters. - Вычисление простейших выражений Javascript. -Выражения не должны содержать фигурных скобок. + Evaluate simple JavaScript statements.<br>The statements may not contain '{' nor '}' characters. + Вычисление простейших выражений JavaScript.<br>Выражения не должны содержать фигурных скобок. @@ -7903,6 +7998,10 @@ to version control (%2) &Functions to insert: &Вставляемые методы: + + Filter + Фильтр + &Hide reimplemented functions &Скрывать реализованные методы @@ -8013,6 +8112,17 @@ to version control (%2) Obj-C++11 + + CppTools::AbstractEditorSupport + + The file name. + Имя файла. + + + The class name. + Имя класса. + + CppTools::CppClassesFilter @@ -9239,33 +9349,6 @@ Flags: %3 Команда CVS - - DebugViewWidget - - Debug - Отладка - - - Model Log - Журнал модели - - - Clear - Очистить - - - Instance Notifications - Уведомления - - - Instance Errors - Ошибки - - - Enabled - Включено - - Debugger @@ -9373,12 +9456,27 @@ Flags: %3 Комплект не найден. + + Debugger::DebuggerMainWindow + + Memory... + Память... + + + Debugger Toolbar + Панель отладчика + + Debugger::DebuggerOptionsPage Not recognized Не определён + + Could not determine debugger type + Не удалось определить тип отладчика + Name Имя @@ -9687,6 +9785,10 @@ Flags: %3 Engine: Отладчик: + + Hit Count: + Число попаданий: + Corrected Line Number: Исправленный номер строки: @@ -10294,7 +10396,7 @@ Flags: %3 Использует каталог для кэширования символов используемых отладчиком. - Setup Symbol Paths... + Set up Symbol Paths... Задать пути к символам... @@ -10577,17 +10679,6 @@ Setting breakpoints by file name and line number may fail. Отладчик: - - Debugger::Internal::DebuggerMainWindowPrivate - - Memory... - Память... - - - Debugger Toolbar - Панель отладчика - - Debugger::Internal::DebuggerPane @@ -11137,14 +11228,6 @@ Qt Creator не может подключиться к нему. <p>This switches the debugger to instruction-wise operation mode. In this mode, stepping operates on single instructions and the source location view also shows the disassembled instructions. <p>Переключает отладчик для работы на уровне инструкций процессора. В этом режиме шаги происходят в пределах одной инструкции, а в окне исходных текстов так же отображается дизассемблированный код. - - Native Mixed Mode - Родной смешанный режим - - - <p>This switches the debugger to native-mixed operation mode. In this mode, stepping and data display will be handled by the native debugger backend (GDB, LLDB or CDB) for C++, QML and JS sources. - <p>Переключает отладчик в родной-смешанный режим работы. В этом режиме шаги и отображение данные обрабатывается родным отладчиком (GDB, LLDB или CDB) для C++, QML и JS. - Dereference Pointers Automatically Автоматически разыменовывать указатели @@ -11562,10 +11645,6 @@ Try: %2 Stopping temporarily Временно остановлено - - Processing queued commands - Обработка очереди команд - GDB not responding GDB не отвечает @@ -11660,10 +11739,6 @@ Try: %2 Stopped. Остановлено. - - Continuing after temporary stop... - Продолжение после временного останова... - GDB I/O Error Ошибка вводы/вывода GDB @@ -12630,6 +12705,10 @@ Do you want to retry? Value Значение + + Edit bits %1...%2 of register %3 + Изменение битов %1...%2 регистра %3 + Debugger::Internal::RegisterMemoryView @@ -12837,6 +12916,14 @@ Do you want to retry? JS-Function: Функция JS: + + Module: + Модуль: + + + Receiver: + Получатель: + Note: Замечание: @@ -13572,6 +13659,11 @@ Do you want to retry? Remove All Expression Evaluators Удалить все вычисляемые выражения + + Change Value Display Format + Это название подменю. + Формат отображения значения + Copy View Contents to Clipboard Скопировать видимое содержимое в буфер обмена @@ -13680,10 +13772,6 @@ Do you want to retry? Select Widget to Add into Expression Evaluator Выбрать виджет для добавления в вычисляемые значения - - Change Local Display Format - Формат отображения - Open Memory Editor Открыть редактор памяти @@ -14053,6 +14141,10 @@ Rebuilding the project might help. Waiting for data... Ожидание данных... + + Failed + Сбой + DiffEditor::Internal::SideDiffEditorWidget @@ -14106,6 +14198,10 @@ Rebuilding the project might help. Waiting for data... Ожидание данных... + + Failed + Сбой + DiffEditor::SideBySideView @@ -14309,6 +14405,13 @@ Rebuilding the project might help. Переместить пол экрана вверх + + EnterTabDesignerAction + + Step into: %1 + Войти в: %1 + + ExtensionSystem::Internal::PluginDetailsView @@ -15460,6 +15563,10 @@ will also disable the following plugins: Details Подробнее + + C&heckout + &Перейти + &Refresh &Обновить @@ -15472,10 +15579,6 @@ will also disable the following plugins: Cherry &Pick Перенести &изменения - - &Checkout - &Перейти - Fetching "%1"... Загружается «%1»... @@ -19758,6 +19861,160 @@ Ids must begin with a lowercase letter. Обычный ассемблер Qt Creator + + ModelEditor::Internal::ActionHandler + + &Remove + &Убрать + + + &Delete + &Удалить + + + Open Parent Diagram + Открыть родительскую диаграмму + + + Edit Element Properties + Изменить свойства элемента + + + Return + Ввод + + + + ModelEditor::Internal::FileWizardFactory + + Model + Модель + + + Creates an empty model + Создаёт пустую модель + + + New %1 + Новая %1 + + + Model Name and Location + Имя модели и размещение + + + Model name: + Имя модели: + + + Location: + Размещение: + + + + ModelEditor::Internal::ModelDocument + + No model loaded. Cannot save. + Модель не загружена. Сохранить невозможно. + + + model.qmodel + model.qmodel + + + Cannot reload model file. + Не удалось загрузить файл модели. + + + Could not open "%1" for reading: %2. + Не удалось открыть файл «%1» для чтения: %2. + + + + ModelEditor::Internal::ModelEditor + + <html><body style="color:#909090; font-size:14px"><div align='center'><div style="font-size:20px">Open a diagram</div><table><tr><td><hr/><div style="margin-top: 5px">&bull; Double-click on diagram in model tree</div><div style="margin-top: 5px">&bull; Select "Open Diagram" from package's context menu in model tree</div></td></tr></table></div></body></html> + <html><body style="color:#909090; font-size:14px"><div align='center'><div style="font-size:20px">Открытие диаграммы</div><table><tr><td><hr/><div style="margin-top: 5px">&bull; Двойной щелчок на диаграмме в дереве модели</div><div style="margin-top: 5px">&bull; «Открыть диаграмму» в контекстном меню пакета дерева модели</div></td></tr></table></div></body></html> + + + Add Package + Добавить пакет + + + Add Component + Добавить компонент + + + Add Class + Добавить класс + + + Add Canvas Diagram + Добавить диаграмму холста + + + Package + Пакет + + + Component + Компонент + + + Class + Класс + + + Item + Элемент + + + Annotation + Аннотация + + + Boundary + Граница + + + + ModelEditor::Internal::ModelsManager + + Open Diagram + Открыть диаграмму + + + + ModelEditor::Internal::PxNodeController + + Add Component %1 + Добавить компонент %1 + + + Add Class %1 + Добавить класс %1 + + + Add Package %1 + Добавить пакет %1 + + + Add Package and Diagram %1 + Добавить пакет и диаграмму %1 + + + Add Component Model + Добавить модель компонентов + + + Create Component Model + Создать модель компонентов + + + Drop Node + Удаление элемента + + ModelManagerSupportInternal::displayName @@ -19765,6 +20022,24 @@ Ids must begin with a lowercase letter. Встроенный в Qt Creator + + ModelNodeOperations + + Go to Implementation + Перейти к реализации + + + Cannot find an implementation. + Не удалось найти реализацию. + + + + Modeling + + Modeling + Моделирование + + MouseAreaSpecifics @@ -19867,6 +20142,24 @@ Ids must begin with a lowercase letter. Python Editor Редактор Python + + Model Editor + Редактор моделей + + + + PathTool + + Path Tool + Кривые + + + + PathToolAction + + Edit Path + Изменить кривую + PathViewSpecifics @@ -20641,6 +20934,10 @@ Ids must begin with a lowercase letter. Name of current project Имя текущего проекта + + Type of current build + Тип текущей сборки + Name of current build Имя текущей сборки @@ -21364,6 +21661,13 @@ Ids must begin with a lowercase letter. Значение параметра среды не верно. + + ProjectExplorer::EnvironmentValidator + + Variable already exists. + Переменная уже существует. + + ProjectExplorer::EnvironmentWidget @@ -21477,6 +21781,14 @@ Ids must begin with a lowercase letter. Close Other Tabs Закрыть другие вкладки + + Increase Font Size + Увеличить шрифт + + + Decrease Font Size + Уменьшить шрифт + Application Output Вывод приложения @@ -21637,6 +21949,14 @@ Ids must begin with a lowercase letter. Compile Output Консоль сборки + + Increase Font Size + Увеличить шрифт + + + Decrease Font Size + Уменьшить шрифт + ProjectExplorer::Internal::CopyTaskHandler @@ -21660,6 +21980,10 @@ Ids must begin with a lowercase letter. ProjectExplorer::Internal::CurrentProjectFind + + Project "%1" + Проект «%1» + Current Project Текущий проект @@ -21816,6 +22140,13 @@ Ids must begin with a lowercase letter. <В этой сессии нет других проектов> + + ProjectExplorer::Internal::DependenciesWidget + + Synchronize active kit, build, and deploy configuration between projects. + Сихронизировать у проектов текущий комплект и конфигурации сборки и установки. + + ProjectExplorer::Internal::DesktopDeviceFactory @@ -22100,6 +22431,13 @@ Ids must begin with a lowercase letter. ScannerGenerator: двоичный шаблон «%1» неверен. + + ProjectExplorer::Internal::JsonWizardFileGenerator + + No 'key' in options object. + Отсутствует «key» в объекте «options». + + ProjectExplorer::Internal::KitEnvironmentConfigWidget @@ -22122,6 +22460,10 @@ Ids must begin with a lowercase letter. Edit Environment Changes Изменение среды + + Enter one variable per line with the variable name separated from the variable value by "=".<br>Environment variables can be referenced with ${OTHER}. + Введите переменные по одной в строке. Имя переменной должно отделяться от значения символом «=».<br>Ссылаться на переменные среды можно с помощью ${OTHER}. + ProjectExplorer::Internal::KitManagerConfigWidget @@ -22424,6 +22766,22 @@ Ids must begin with a lowercase letter. <i>jom</i> is a drop-in replacement for <i>nmake</i> which distributes the compilation process to multiple CPU cores. The latest binary is available at <a href="http://download.qt.io/official_releases/jom/">http://download.qt.io/official_releases/jom/</a>. Disable it if you experience problems with your builds. <i>jom</i> - это замена <i>nmake</i>, распределяющая компиляцию на несколько ядер процессора. Свежайшая сборка доступна по адресу <a href="http://download.qt.io/official_releases/jom/">http://download.qt.io/official_releases/jom/</a>. В случае проблем со сборкой отключите использование jom. + + Stop applications before building: + Останавливать приложение перед сборкой: + + + None + Никогда + + + Same Project + Тот же проект + + + All + Всегда + ProjectExplorer::Internal::ProjectFileFactory @@ -23021,6 +23379,21 @@ to project "%2". Аннотация с использованием системы контроля версий. + + ProjectExplorer::Internal::WaitForStopDialog + + Waiting for Applications to Stop + Ожидание завершения приложения + + + Cancel + Отмена + + + Waiting for applications to stop. + Ожидание завершения приложения. + + ProjectExplorer::Internal::WinCEToolChainConfigWidget @@ -23242,8 +23615,8 @@ to project "%2". Объект «data» для страницы «Форма» должен быть не задан или пустым. - Check whether a variable exists. Returns "true" if it does and an empty string if not. - Проверка существования переменной. В случае успеха возвращает «true», иначе пустую строку. + Check whether a variable exists.<br>Returns "true" if it does and an empty string if not. + Проверка существования переменной.<br>В случае успеха возвращает «true», иначе пустую строку. Could not determine target path. "TargetPath" was not set on any page. @@ -23338,8 +23711,8 @@ to project "%2". Объект «data» должен быть пустым или иметь тип JSON для страниц «Проект». - "data" for a "Summary" page needs to be unset or an empty object. - Объект «data» для страницы «Общее» должен быть не задан или пустым. + "data" for a "Summary" page can be unset or needs to be an object. + «data» для страницы «Общее» может быть не задан или должен быть объектом. "data" must be a JSON object for "VcsConfiguration" pages. @@ -23351,6 +23724,686 @@ to project "%2". Do not translate "VcsConfiguration", because it is the id of a page. Странице «VcsConfiguration» требуется заданный «vcsId». + + Class name: + Имя класса: + + + <Custom> + <Особый> + + + Base class: + Базовый класс: + + + %{BaseCB} + %{BaseCB} + + + Include QObject + Подключить QObject + + + Include QWidget + Подключить QWidget + + + Include QMainWindow + Подключить QMainWindow + + + Include QDeclarativeItem - Qt Quick 1 + Подключить QDeclarativeItem - Qt Quick 1 + + + Include QQuickItem - Qt Quick 2 + Подключить QQuickItem - Qt Quick 2 + + + Include QSharedData + Подключить QSharedData + + + %{JS: Cpp.classToFileName('%{Class}', '%{JS: Util.preferredSuffix('text/x-c++hdr')}')} + %{JS: Cpp.classToFileName('%{Class}', '%{JS: Util.preferredSuffix('text/x-c++hdr')}')} + + + Header file: + Заголовочный файл: + + + %{JS: Cpp.classToFileName('%{Class}', '%{JS: Util.preferredSuffix('text/x-c++src')}')} + %{JS: Cpp.classToFileName('%{Class}', '%{JS: Util.preferredSuffix('text/x-c++src')}')} + + + Source file: + Файл исходных текстов: + + + Path: + Путь: + + + Define Class + Определить класс + + + Details + Подробнее + + + Project Management + Управление проектом + + + Summary + Итог + + + Creates a C++ header and a source file for a new class that you can add to a C++ project. + Создание заголовочного и исходного файлов C++ для нового класса, добавляемого в проект C++. + + + C++ + C++ + + + C++ Class + Класс C++ + + + Customize header row + Настроить строку заголовка + + + Items are editable + Элементы можно менять + + + Rows and columns can be added + Строки и столбцы можно добавлять + + + Rows and columns can be removed + Строки и столбцы можно удалять + + + Fetch data dynamically + Подгружать данные + + + Define Item Model Class + Определить класс модели элемента + + + Creates a Qt item model. + Создание модели элемента Qt. + + + Qt + Qt + + + Qt Item Model + Модель элемента Qt + + + Import QtCore + Импортировать QtCore + + + Import QtWidgets + Импортировать QtWidgets + + + Import QtQuick + Импортировать QtQuick + + + %{JS: Util.fileName('%{Class}', '%{JS: Util.preferredSuffix('text/x-python')}')} + %{JS: Util.fileName('%{Class}', '%{JS: Util.preferredSuffix('text/x-python')}')} + + + Creates new Python class file. + Создание нового файла с классом Python. + + + Python + Python + + + Python Class + Класс на Python + + + Component name: + Имя компоненты: + + + %{Class}Form + %{Class}Form + + + Component form name: + Имя формы компонента: + + + Creates a Qt Quick Designer UI form along with a matching QML file for implementation purposes. You can add the form and file to an existing Qt Quick Project. + Создание формы Qt Quick Designer вместе с файлом QML для реализации поведения. Их можно добавить в существующий проект Qt Quick. + + + QtQuick UI File + Файл QtQuick UI + + + Location + Размещение + + + Creates a C++ header file that you can add to a C++ project. + Создание заголовочного файла С++, который можно добавить в проект С++. + + + C++ Header File + Заголовочный файл С++ + + + Creates a C++ source file that you can add to a C++ project. + Создание файла исходных текстов С++, который можно добавить в проект С++. + + + C++ Source File + Файл исходных текстов C++ + + + Choose a Form Template + Выбор шаблона формы + + + Form Template + Шаблон формы + + + Creates a Qt Designer form that you can add to a Qt Widget Project. This is useful if you already have an existing class for the UI business logic. + Создание формы Qt Designer, которую можно добавить в проект Qt Widget. Имеет смысл, если у вас уже есть класс с бизнес-логикой. + + + Qt Designer Form + Форма Qt Designer + + + Creates a Java file with boilerplate code. + Создание файла Java с шаблонным кодом. + + + Java + Java + + + Java File + Файл Java + + + Stateless library + Библиотека без состояния + + + Options + Параметры + + + Creates a JavaScript file. + Создание файла JavaScript. + + + JS File + Файл JS + + + Creates an empty Python script file using UTF-8 charset. + Создание пустого файла сценария Python с использованием кодировки UTF-8. + + + Python File + Файл Python + + + Creates a Qt Resource file (.qrc). + Создание файла ресурсов Qt (.qrc). + + + Qt Resource File + Файл ресурсов Qt + + + Creates a QML file with boilerplate code, starting with "import QtQuick 1.1". + Создание файла QML с шаблонным кодом, начинающимся с «import QtQuick 1.1». + + + QML File (Qt Quick 1) + Файл QML (Qt Quck 1) + + + Creates a QML file with boilerplate code, starting with "import QtQuick 2.0". + Создание файла QML с шаблонным кодом, начинающимся с «import QtQuick 2.0». + + + QML File (Qt Quick 2) + Файл QML (Qt Quck 2) + + + Creates a scratch buffer using a temporary file. + Создание рабочего буфера с использованием временного файла. + + + General + Общее + + + Scratch Buffer + Рабочий буфер + + + Creates an empty file. + Создание пустого файла. + + + Empty File + Пустой файл + + + Project Location + Размещение проекта + + + qmake + qmake + + + CMake + CMake + + + Qbs + Qbs + + + Build system: + Система сборки: + + + Define Build System + Выбор системы сборки + + + Build System + Система сборки + + + Kit Selection + Выбор комплекта + + + Kits + Комплекты + + + Creates a simple C application using either qmake, CMake, or Qbs to build. + Создание простого приложения на языке C с использованием qmake, CMake или Qbs для сборки. + + + Non-Qt Project + Проект без Qt + + + Plain C Application + Приложение на языке С + + + Creates a simple C++ application using either qmake, CMake, or Qbs to build. + Создание простого приложения на языке C++ с использованием qmake, CMake или Qbs для сборки. + + + Plain C++ Application + Приложение на языке С++ + + + This wizard creates a simple Qt-based console application. + Этот мастер создаст простое консольное приложение на базе Qt. + + + Creates a project containing a single main.cpp file with a stub implementation. + +Preselects a desktop Qt for building the application if available. + Создание проекта, содержащего единственный файл main.cpp с минимальным кодом. + +При наличии выбирает профиль Desktop Qt для сборки приложения. + + + Application + Приложение + + + Qt Console Application + Консольное приложение Qt + + + This wizard creates an empty .pro file. + Этот мастер создаст пустой файл .pro. + + + Creates a qmake-based project without any files. This allows you to create an application without any default classes. + Создание пустого проекта на базе qmake. Это позволит создать приложение без каких-либо классов. + + + Other Project + Другой проект + + + Empty qmake Project + Пустрой проект qmake + + + Create a three.js based application. + Создание приложение на базе three.js. + + + Define Project Details + Определение данных проекта + + + Creates a Qt Canvas 3D QML project. Optionally including three.js. + Создание проекта Qt Canvas 3D QML. Возможно подключение three.js. + + + Qt Canvas 3D Application + Приложение Qt Canvas 3D + + + Qt 5.5 + Qt 5.5 + + + Qt 5.4 + Qt 5.4 + + + Qt 5.3 + Qt 5.3 + + + Minimal required Qt version: + Минимально необходимая версия Qt: + + + With ui.qml file + С файлом ui.qml + + + Creates a deployable Qt Quick 2 application. + Создание устанавливаемого приложения Qt Quick 2. + + + Qt Quick Application + Приложение Qt Quick + + + Enable native styling. Requires dependency on the QtWidgets module. + Включить родной стиль. Зависит от модуля QtWidgets. + + + Creates a deployable Qt Quick 2 application using Qt Quick Controls. + Создание устанавливаемого приложения Qt Quick 2 с использованием Qt Quick Controls. + + + Qt Quick Controls Application + Приложение Qt Quick Controls + + + Creates a Qt Quick 2 UI project with a QML entry point. To use it, you need to have a QML runtime environment such as qmlscene set up. Consider using a Qt Quick Application project instead. + Создание проекта Qt Quick 2 UI с запуском через QML. Для его использования необходима установка среды выполнения QML, например qmlscene. Рассмотрите возможность использования «Приложение Qt Quick» . + + + Qt Quick UI + Интерфейс Qt Quick + + + Creates a Qt Quick 2 UI project using Qt Quick Controls with a QML entry point. To use it, you need to have a QML runtime environment such as qmlscene set up. Consider using a Qt Quick Controls Application project instead. + Создание проекта Qt Quick 2 UI с исспользованием Qt Quick Controls с запуском через QML. Для его использования необходима установка среды выполнения QML, например qmlscene. Рассмотрите возможность использования «Приложение Qt Quick Controls» . + + + Qt Quick Controls UI + Интерфейс Qt Quick Controls + + + Configuration + Конфигурация + + + Please configure <b>%{vcsName}</b> now. + Требуется конфигурирование <b>%{vcsName}</b>. + + + Repository: + Хранилище: + + + %{defaultDir} + %{defaultDir} + + + Directory: + Каталог: + + + "%{JS: Util.toNativeSeparators('%{TargetPath}')}" exists in the filesystem. + «%{JS: Util.toNativeSeparators('%{TargetPath}')}» уже существует. + + + Use existing directory + Использовать существующий каталог + + + Proceed with cloning the repository, even if the target directory already exists. + Выполнять клонирование хранилища, даже если целевой каталог уже существует. + + + Stacked + Уложеная + + + Make the new branch depend on the availability of the source branch. + Сделать новую ветку зависимой от доступности исходной ветки. + + + Standalone + Отдельное + + + Do not use a shared repository. + Не использовать общее хранилище. + + + Bind new branch to source location + Привязать новую ветку к размещению исходников + + + Bind the new branch to the source location. + Привязать новую ветку к размещению исходников. + + + Switch checkout + Сменить выгружаемый каталог + + + Switch the checkout in the current directory to the new branch. + Переключить выгружаемый каталог текущего каталога на новую. + + + Hardlink + Жёсткая ссылка + + + Use hard-links in working tree. + Использовать жёсткий ссылки в рабочей копии. + + + No working-tree + Без рабочей копии + + + Do not create a working tree. + Не создавать рабочую копию. + + + Revision: + Ревизия: + + + Specify repository URL, checkout directory, and path. + Укажите URL хранилища, выгружаемый каталог и путь. + + + Running Bazaar branch... + Выполнение смены ветки Bazaar... + + + Clone + Клонировать + + + Clones a Bazaar branch and tries to load the contained project. + Клонирование ветки Bazaar и попытка загрузки имеющегося там проекта. + + + Import Project + Импортировать проект + + + Bazaar Clone (Or Branch) + Клонирование Bazaar + + + Module: + Модуль: + + + Specify module and checkout directory. + Укажите модуль и выгружаемый каталог. + + + Running CVS checkout... + Выполнение выгрузки CVS... + + + Checkout + Выгрузка + + + Checks out a CVS repository and tries to load the contained project. + Выгрузка хранилища CVS и попытка загрузки имеющегося там проекта. + + + CVS Checkout + Извлечь из CVS + + + <default branch> + <ветка по умолчанию> + + + Branch: + Ветка: + + + Recursive + Рекурсивно + + + Recursively initialize submodules. + Рекурсивно инициализировать модули. + + + Specify repository URL, branch, checkout directory, and path. + Укажите URL хранилища, выгружаемый каталог и путь. + + + Running Git clone... + Выполнение клонирования Git... + + + Clones a Git repository and tries to load the contained project. + Клонирование хранилища Git и попытка загрузки имеющегося там проекта. + + + Git Clone + Клонирование Git + + + Running Mercurial clone... + Выполнение клонирования Mercurial... + + + Clones a Mercurial repository and tries to load the contained project. + Клонирование хранилища Mercurial и попытка загрузки имеющегося там проекта. + + + Mercurial Clone + Клонирование Mercurial + + + Trust Server Certificate + Доверять сертификату сервера + + + Running Subversion checkout... + Выполнение выгрузки Subversion... + + + Checks out a Subversion repository and tries to load the contained project. + Выгрузка хранилища Subversion и попытка загрузки имеющегося там проекта. + + + Subversion Checkout + Извлечь из Subversion + + + Creates a fragment shader in the Desktop OpenGL Shading Language (GLSL). Fragment shaders generate the final pixel colors for triangles, points and lines rendered with OpenGL. + Создание фрагментного шейдера на языке Desktop OpenGL Shading Language (GLSL). Фрагментные шейдеры создают конечный цвет пикселя для теугольников, точек и линий, отрисовываемых с помощью OpenGL. + + + GLSL + GLSL + + + Fragment Shader (Desktop OpenGL) + Фрагментный шейдер (Desktop OpenGL) + + + Creates a vertex shader in the Desktop OpenGL Shading Language (GLSL). Vertex shaders transform the positions, normals and texture co-ordinates of triangles, points and lines rendered with OpenGL. + Создание вершинного шейдера на языке Desktop OpenGL Shading Language (GLSL). Вершинные шейдеры изменяют положение, нормали и текстурные координаты теугольников, точек и линий, отрисовываемых с помощью OpenGL. + + + Vertex Shader (Desktop OpenGL) + Вершинный шейдер (Desktop OpenGL) + + + Creates a fragment shader in the OpenGL/ES 2.0 Shading Language (GLSL/ES). Fragment shaders generate the final pixel colors for triangles, points and lines rendered with OpenGL. + Создание фрагментного шейдера на языке OpenGL/ES 2.0 Shading Language (GLSL/ES). Фрагментные шейдеры создают конечный цвет пикселя для теугольников, точек и линий, отрисовываемых с помощью OpenGL. + + + Fragment Shader (OpenGL/ES 2.0) + Фрагментный шейдер (OpenGL/ES 2.0) + + + Creates a vertex shader in the OpenGL/ES 2.0 Shading Language (GLSL/ES). Vertex shaders transform the positions, normals and texture co-ordinates of triangles, points and lines rendered with OpenGL. + Создание вершинного шейдера на языке OpenGL/ES 2.0 Shading Language (GLSL/ES). Вершинные шейдеры изменяют положение, нормали и текстурные координаты теугольников, точек и линий, отрисовываемых с помощью OpenGL. + + + Vertex Shader (OpenGL/ES 2.0) + Вершинный шейдер (OpenGL/ES 2.0) + ProjectExplorer::JsonWizardFactory @@ -23502,10 +24555,6 @@ to project "%2". When parsing "options": Key "%1" set more than once. При обработке «options»: Ключ «%1» задан более одного раза. - - Value for "options" is not a list. - Значение «options» не является списком. - ProjectExplorer::JsonWizardGenerator @@ -23856,10 +24905,6 @@ Please close all running instances of your application before starting a build.< The name of the currently active kit. Название активного комплекта. - - The id of the currently active kit. - Идентификатор активного комплекта. - Cancel Build && Unload Отменить сборку и выгрузить @@ -23880,14 +24925,6 @@ Please close all running instances of your application before starting a build.< Do you want to cancel the build process and unload the project anyway? Остановить процесс сборки и выгрузить проект? - - Failed opening project "%1": Project already open. - Не удалось открыть проект «%1»: проект уже открыт. - - - Failed opening project "%1": Settings could not be restored. - Не удалось открыть проект «%1»: невозможно восстановить настройки. - Failed opening project "%1": No plugin can open project type "%2". Не удалось открыть проект «%1»: нет модуля для открытия проектов типа «%2». @@ -23908,6 +24945,14 @@ Please close all running instances of your application before starting a build.< The configuration that was supposed to run is no longer available. Предполагаемая для запуска конфигурация больше не доступна. + + Stop Applications + Остановка приложений + + + Stop these applications before building? + Остановить эти приложения перед сборкой? + The project %1 is not configured, skipping it. Проект %1 не настроен, пропущен. @@ -23973,6 +25018,22 @@ Please close all running instances of your application before starting a build.< Deleting File Failed Не удалось удалить файл + + The project file %1 cannot be automatically changed. + +Rename %2 to %3 anyway? + Невозможно автоматически изменить файл проекта %1. + +Переименовать %2 в %3? + + + The file %1 could not be renamed %2. + Не удалось переименовать файл %1 в %2. + + + Cannot Rename File + Не удалось переименовать файл + Delete File... Удалить файл... @@ -23989,6 +25050,14 @@ Please close all running instances of your application before starting a build.< Ctrl+T Ctrl+T + + The name of the currently active kit as a filesystem-friendly version. + Название активного комплекта в пригодной для файловой системы форме. + + + The ID of the currently active kit. + Идентификатор активного комплекта. + Load Project Загрузить проект @@ -24052,10 +25121,6 @@ Do you want to ignore them? Full build path of the current project's active build configuration. Полный путь к каталогу сборки активной конфигурации текущего проекта. - - The name of the currently active kit in a filesystem-friendly version. - Название активного комплекта в пригодной для файловой системы форме. - The host address of the device in the currently active kit. Адрес хоста устройства текущего комплекта. @@ -24084,26 +25149,10 @@ Do you want to ignore them? Name of current session. Название текущей сессии. - - Failed to open project. - Не удалось открыть проект. - - - debug - отладка - The username with which to log into the device in the currently active kit. Имя пользователя для входа на устройство текущего комплекта. - - release - выпуск - - - unknown - неизвестно - All Projects Все проекты @@ -24112,6 +25161,10 @@ Do you want to ignore them? Failed to Open Project Не удалось открыть проект + + <h3>Project already open</h3> + <h3>Проект уже открыт</h3> + Unknown error Неизвестная ошибка @@ -24356,10 +25409,6 @@ These files are preserved. Remove projects from Session Удалить проекты из сессии - - Failed to open project - Не удалось открыть проект - Loading Session Загрузка сессии @@ -24591,21 +25640,6 @@ These files are preserved. <Нет> - - PuppetBuildProgressDialog - - Build Progress - Состояние сборки - - - Build Adapter for the current Qt. Happens only once for every Qt installation. - Собирается адаптер для текущего Qt. Требуется только один раз для каждого Qt. - - - Use Fallback QML Emulation Layer - Использовать резервный эмулятор QML - - PuppetCreator @@ -24633,13 +25667,6 @@ These files are preserved. Версия Qt не поддерживается - - PuppetDialog - - Dialog - - - PythonEditor::Internal::PythonProjectManager @@ -24739,6 +25766,66 @@ These files are preserved. <Enter regular expression to exclude> <Введите регулярное выражение> + + Show Definition + Показать определение + + + Open Diagram + Открыть диаграмму + + + Create Diagram + Создать диаграмму + + + Remove + Убрать + + + Delete + Удалить + + + Align Objects + Выровнить объекты + + + Align Left + По левому краю + + + Center Vertically + По центру вертикально + + + Align Right + По правому краю + + + Align Top + По верху + + + Center Horizontally + По центру горизонтально + + + Align Bottom + По низу + + + Same Width + По ширине + + + Same Height + По высоте + + + Same Size + По размеру + QSsh::Internal::SftpChannelPrivate @@ -25060,6 +26147,10 @@ These files are preserved. The public key file could not be saved: %1 Не удалось сохранить открытый ключ: %1 + + ECDSA + ECDSA + Qbs @@ -25989,6 +27080,18 @@ The files in the Android package source directory are copied to the build direct Use debug version of frameworks (DYLD_IMAGE_SUFFIX=_debug) Использовать отладочные версии библиотек (DYLD_IMAGE_SUFFIX=_debug) + + Add build library search path to DYLD_LIBRARY_PATH and DYLD_FRAMEWORK_PATH + Добавить путь к собираемой библитеке в DYLD_LIBRARY_PATH и DYLD_FRAMEWORK_PATH + + + Add build library search path to PATH + Добавить путь к собираемой библитеке в PATH + + + Add build library search path to LD_LIBRARY_PATH + Добавить путь к собираемой библитеке в LD_LIBRARY_PATH + QmakeProjectManager::Internal::DetailsPage @@ -26407,9 +27510,9 @@ Neither the path to the library nor the path to its includes is added to the .pr %1 уже является каталогом сборки другого проекта. Содержимое будет перезаписано. - An incompatible build exists in %1, which will be overwritten. - %1 build directory - В %1 обнаружена несовместимая сборка. Она будет замещена. + %1 The build in %2 will be overwritten. + %1 error message, %2 build directory + %1 Файлы сборки в %2 будут заменены. @@ -26715,6 +27818,26 @@ Neither the path to the library nor the path to its includes is added to the .pr QmakeProjectManager::QmakeBuildConfiguration + + Could not parse Makefile. + Не удалось разобрать Makefile. + + + The Makefile is for a different project. + Makefile от другого проекта. + + + The build type has changed. + Тип сборки изменился. + + + The qmake arguments have changed. + Параметры qmake изменились. + + + The mkspec has changed. + Изменился mkspec. + Parsing the .pro file Разбор файла .pro @@ -26745,8 +27868,15 @@ Neither the path to the library nor the path to its includes is added to the .pr Debug - Build - Сборка + Profile + The name of the profile build configuration created by default for a qmake project. + Профилирование + + + Profile + Shadow build directory suffix + Non-ASCII characters in directory suffix may cause build issues. + Profile @@ -26958,6 +28088,13 @@ Neither the path to the library nor the path to its includes is added to the .pr Добавить вкладку: + + QmlDesigner::ColorTool + + Color Tool + Цвет + + QmlDesigner::ComponentAction @@ -26965,6 +28102,13 @@ Neither the path to the library nor the path to its includes is added to the .pr Изменить компоненты определённые в этом файле. + + QmlDesigner::ConnectionViewWidget + + Connections + Подключения + + QmlDesigner::CrumbleBar @@ -26976,6 +28120,33 @@ Neither the path to the library nor the path to its includes is added to the .pr Всегда сохранять при выходе из субкомпонента + + QmlDesigner::DebugViewWidget + + Debug + Отладка + + + Model Log + Журнал модели + + + Clear + Очистить + + + Instance Notifications + Уведомления + + + Instance Errors + Ошибки + + + Enabled + Включено + + QmlDesigner::FormEditorView @@ -27001,10 +28172,6 @@ Neither the path to the library nor the path to its includes is added to the .pr Show bounding rectangles and stripes for empty items (A). Показавать границы и контуры пустых объектов (A). - - Only select items with content (S). - Выделять только элементы с содержимым (S). - Width Ширина @@ -27032,6 +28199,92 @@ Neither the path to the library nor the path to its includes is added to the .pr Управление импортом + + QmlDesigner::Internal::BindingModel + + Item + Элемент + + + Property + Свойство + + + Source Item + Исходный элемент + + + Source Property + Исходное свойство + + + Error + Ошибка + + + + QmlDesigner::Internal::ConnectionDelegate + + Change to default state + Перевести в исходное состояние + + + Change state to %1 + Перевести в состояние %1 + + + + QmlDesigner::Internal::ConnectionModel + + Target + Цель + + + Signal Handler + Обработчик сигналов + + + Action + Действие + + + Error + Ошибка + + + + QmlDesigner::Internal::ConnectionView + + Connection View + Обзор соединений + + + + QmlDesigner::Internal::ConnectionViewWidget + + Connections + Title of connection view + Соединения + + + Bindings + Title of connection view + Привязки + + + Dynamic Properties + Title of dynamic properties view + Динамические свойства + + + Add binding or connection. + Добавление привязки или соединения. + + + Remove selected binding or connection. + Удаление выбранной привязки или соединения. + + QmlDesigner::Internal::DebugView @@ -27039,11 +28292,11 @@ Neither the path to the library nor the path to its includes is added to the .pr Модель подключена - FileName %1 + Filename %1 Файл %1 - DebugView is enabled + Debug view is enabled DebugView включён @@ -27066,9 +28319,65 @@ Neither the path to the library nor the path to its includes is added to the .pr Node created: Элемент создан: + + Child node: + Дочерний элемент: + + + Property change flag + Флаг изменения свойства + + + Node reparented: + Элемент сменил родителя: + + + New id: + Новый id: + + + Old id: + Старый id: + + + Variant properties changed: + Изменились вариантные свойства: + + + Binding properties changed: + Изменились свойства привязок: + + + Signal handler properties changed: + Изменились свойства обработчика сигнала: + + + Auxiliary data changed: + Изменились дополнительные данные: + + + Instance completed + Экземпляр готов + + + Instance's children changed: + Изменены подэлементы экземпляра: + + + Custom notification: + Особое оповещение: + + + Node source changed: + Изменены исходники элемента: + Node removed: - Элемент удалён: + Удалён элемент: + + + Node about to be removed: + Удаляемый элемент: New parent property: @@ -27078,38 +28387,10 @@ Neither the path to the library nor the path to its includes is added to the .pr Old parent property: Старое родительское свойство: - - PropertyChangeFlag - PropertyChangeFlag - - - Node reparanted: - Элемент сменил родителя: - - - New Id: - Новый Id: - - - Old Id: - Старый Id: - Node id changed: Изменён id элемента: - - VariantProperties changed: - Изменены VariantProperties: - - - BindingProperties changed: - Изменены BindingProperties: - - - SignalHandlerProperties changed: - Изменены SignalHandlerProperties: - Node selected: Выбран элемент: @@ -27118,10 +28399,6 @@ Neither the path to the library nor the path to its includes is added to the .pr Properties removed: Удалённые свойства: - - Auxiliary Data Changed: - Изменённые дополнительные данные: - Begin rewriter transaction Начало транзакционного рефакторинга @@ -27139,16 +28416,8 @@ Neither the path to the library nor the path to its includes is added to the .pr Изменение свойства экземпляра - Instance Completed - Экземпляр готов - - - Custom Notification: - Особое оповещение: - - - Node Source Changed: - Изменены исходники элемента: + parent: + родитель: @@ -27188,6 +28457,37 @@ Neither the path to the library nor the path to its includes is added to the .pr Internal error (%1) Внутренняя ошибка (%1) + + Cannot open this QML document because of an error in the QML file: + + + Не удалось открыть документ QML, так как файл содержит ошибку: + + + + + + QmlDesigner::Internal::DynamicPropertiesModel + + Item + Элемент + + + Property + Свойство + + + Property Type + Тип свойства + + + Property Value + Значение свойства + + + Error + Ошибка + QmlDesigner::Internal::MetaInfoPrivate @@ -27252,6 +28552,14 @@ Neither the path to the library nor the path to its includes is added to the .pr Qt Quick Designer Дизайнер Qt Quick + + Restart Required + Требуется перезапуск + + + The QML emulation layer path changes will take effect after a restart of the QML Emulation layer or Qt Creator. + Изменения в путях слоя эмуляции QML требуют перезапуска эмулятора QML или Qt Creator. + Canvas Холст @@ -27296,14 +28604,6 @@ Neither the path to the library nor the path to its includes is added to the .pr Sibling item spacing: Отступ между соседними элементами: - - Warns about QML features which are not properly supported by the Qt Quick Designer. - Предупреждать об особенностях QML, которые не поддерживаются корректно Qt Quick Designer. - - - Also warns in the code editor about QML features which are not properly supported by the Qt Quick Designer. - Также предупреждать в редакторе кода об особенностях QML, которые не поддерживаются корректно Qt Quick Designer. - Subcomponents Субкомпоненты @@ -27317,12 +28617,60 @@ Neither the path to the library nor the path to its includes is added to the .pr Слой эмуляции QML - If you do not select this checkbox, Qt Quick Designer uses the QML emulation layer (QML Puppet) built with the Qt configured in the build and run kit for the project. - Если опция отключена, то Qt Quick Designer будет использовать слой эмуляции QML (QML Puppet) поставляемый с выбранным в проекте профилем Qt. + Styling + Стилизация - Always use the QML emulation layer provided by Qt Creator - Всегда использовать слой эмуляции QML предоставляемый Qt Creator + Controls style: + Стиль интерфейса: + + + Default style + По умолчанию + + + Reset Style + Сбросить + + + If you select this radio button, Qt Quick Designer always uses the QML emulation layer (QML Puppet) located at the following path. + При выборе этого режима Qt Quick Designed будет всегда использовать слой эмуляции QML (QML Puppet), размещённых по указанному пути. + + + Use fallback QML emulation layer + Использовать запасной слой эмуляции QML + + + Path: + Путь: + + + Resets the path to the QML emulation layer that comes with Qt Creator. + Вернуть путь к слою эмуляции QML, который идёт в составе Qt Creator. + + + Reset Path + Сбросить путь + + + Use QML emulation layer that is built by the selected Qt + Использовать слой эмуляции QML, собранный выбранным Qt + + + Top level build path: + Верхний уровень каталога сборки: + + + Warns about QML features that are not properly supported by the Qt Quick Designer. + Предупреждать об особенностях QML, которые не поддерживаются корректно Qt Quick Designer. + + + Also warns in the code editor about QML features that are not properly supported by the Qt Quick Designer. + Предупреждать в редакторе об особенностях QML, которые не поддерживаются корректно Qt Quick Designer. + + + Path where Qt Creator can find the QML emulation layer executable (qmlpuppet). + Путь, по которому Qt Creator сможет найти программу слоя эмуляции QML (qmlpuppet). @@ -27359,22 +28707,6 @@ Neither the path to the library nor the path to its includes is added to the .pr Library search input hint text <Фильтр> - - I - I - - - Manage imports for components. - Управление импортом компонентов. - - - Basic Qt Quick only - Только базовый Qt Quick - - - Meego Components - Компоненты Meego - QmlDesigner::NavigatorTreeModel @@ -27382,6 +28714,16 @@ Neither the path to the library nor the path to its includes is added to the .pr Unknown item: %1 Неизвестный элемент: %1 + + Toggles whether this item is exported as an alias property of the root item. + Включает экспорт элемента, как свойства alias корневого элемента. + + + Toggles the visibility of this item in the form editor. +This is independent of the visibility property in QML. + Включает видимость элемента в редакторе форм. +Не зависит от свойства видимости в QML. + Invalid Id Неверный идентификатор @@ -27438,6 +28780,25 @@ Neither the path to the library nor the path to its includes is added to the .pr Эмуляция завершилась крахом при записи puppet-потока. Рекомендуется переоткрыть Qt Quick Designer и запустить запись снова. + + QmlDesigner::PathItem + + Closed Path + Ближайший путь + + + Split Segment + Разделить сегмент + + + Make Curve Segment Straight + Спрямить сегмент кривой + + + Remove Edit Point + Удалить узел + + QmlDesigner::PluginManager @@ -27464,6 +28825,36 @@ Neither the path to the library nor the path to its includes is added to the .pr %1 уже существует. + + QmlDesigner::PuppetBuildProgressDialog + + Build Progress + Состояние сборки + + + Build Adapter for the current Qt. Happens only once for every Qt installation. + Собирается адаптер для текущего Qt. Требуется только один раз для каждого Qt. + + + Open error output file + Открыть файл вывода ошибок + + + Use Fallback QML Emulation Layer + Использовать резервный эмулятор QML + + + OK + Закрыть + + + + QmlDesigner::PuppetDialog + + Dialog + + + QmlDesigner::QmlDesignerPlugin @@ -27471,6 +28862,21 @@ Neither the path to the library nor the path to its includes is added to the .pr Переключить текст/дизайн + + QmlDesigner::QmlWarningDialog + + Warning + Предупреждение + + + This QML file contains features which are not supported by Qt Quick Designer + Этот файл QML содержит свойства, не поддерживаемые Qt Quick Designer + + + Warn about unsupported features + Предупреждать о неподдерживаемых функциях + + QmlDesigner::RewriterView @@ -27589,6 +28995,17 @@ Neither the path to the library nor the path to its includes is added to the .pr Закрыть другие + + QmlDesigner::SourceTool + + Open File + Открытие файла + + + Source Tool + Источник + + QmlDesigner::StatesEditorModel @@ -27704,6 +29121,10 @@ Neither the path to the library nor the path to its includes is added to the .pr Select Parent: %1 Выделить владельца: %1 + + Position + Положение + Deselect: Снять выделение: @@ -27748,25 +29169,38 @@ Neither the path to the library nor the path to its includes is added to the .pr Reset Position Сбросить позицию + + Go to Implementation + Перейти к реализации + Reset z Property Сбросить свойство z - Layout in Column (Positioner) - Компоновать в колонку (позиционер) + Position in Column + Разместить в столбец - Layout in Row (Positioner) - Компоновать в строку (позиционер) + Position in Row + Разместить в строку - Layout in Grid (Positioner) - Компоновать по сетке (позиционер) + Position in Grid + Разместить по сетке - Layout in Flow (Positioner) - Перетекающая компоновка (позиционер) + Position in Flow + как перевести режим Flow не знаю (это аналог грида с фиксированными размерами виджетов, при изменении размеров которого виджет из начала следующей строки перемещается в конец предыдущей, а остальные сдвигаются на его место - как текст в редакторе) + Разместить во Flow + + + Remove Positioner + Удалить размещение + + + Remove Layout + Удалить компоновку Layout in ColumnLayout @@ -27804,10 +29238,6 @@ Neither the path to the library nor the path to its includes is added to the .pr Reset Сбросить - - Step into: %1 - Войти в: %1 - QmlDumpBuildTask @@ -27885,17 +29315,17 @@ Neither the path to the library nor the path to its includes is added to the .pr QmlJS::Link - file or directory not found - файл или каталог не найден + File or directory not found. + Файл или каталог не найден. - QML module not found + QML module not found. Import paths: %1 For qmake projects, use the QML_IMPORT_PATH variable to add import paths. -For qbs projects, declare and set a qmlImportPaths property in your product to add import paths. +For Qbs projects, declare and set a qmlImportPaths property in your product to add import paths. For qmlproject projects, use the importPaths property to add import paths. Модуль QML не найден @@ -27904,7 +29334,7 @@ For qmlproject projects, use the importPaths property to add import paths. @@ -29052,16 +30482,32 @@ Do you want to retry? Debug connection closed Отладочное соединение закрыто + + Debug connection error %1 + Ошибка отладочного подключения %1 + Failed to connect! Не удалось подключиться! - QmlProfiler::Internal::QmlProfilerDataState + QmlProfiler::Internal::QmlProfilerConfigWidget - Trying to set unknown state in events list. - Попытка установить неизвестное состояние в списке событий. + Flush data while profiling: + Передавать данные при профилировании: + + + Periodically flush pending data to Qt Creator. This reduces the delay when loading the +data and the memory usage in the application. It distorts the profile as the flushing +itself takes time. + Периодически передавать очередные данные Qt Creator. Это уменьшает задержку +загрузки данных и использование памяти приложением, но искажает результат +профилирования, так как передача сама по себе занимает время. + + + Flush interval (ms): + Интервал передачи (мс): @@ -29162,14 +30608,6 @@ references to elements in other files, loops, and so on.) Show Full Range Показать весь диапазон - - Show JavaScript Events - Показывать события JavaScript - - - Show QML Events - Показывать события QML - QmlProfiler::Internal::QmlProfilerFileReader @@ -29178,6 +30616,17 @@ references to elements in other files, loops, and so on.) Ошибка разбора файла данных трассировки: %1 + + QmlProfiler::Internal::QmlProfilerOptionsPage + + QML Profiler + Профайлер QML + + + Analyzer + Анализатор + + QmlProfiler::Internal::QmlProfilerPlugin @@ -29222,6 +30671,10 @@ references to elements in other files, loops, and so on.) Application stopped before loading all data Приложение остановлено до загрузки всех данных + + Waiting for data + Ожидание данных + QmlProfiler::Internal::QmlProfilerTool @@ -29245,14 +30698,6 @@ references to elements in other files, loops, and so on.) Save QML Trace Сохранить трассировку QML - - The QML profiler requires Qt 4.7.4 or newer. -The Qt version configured in your active build configuration is too old. -Do you want to continue? - Профайлеру QML требуется Qt версии 4.7.4 или выше. -Версия Qt настроенная для текущей конфигурации сборки слишком старая. -Продолжить? - %1 s %1 сек @@ -29289,6 +30734,10 @@ Do you want to save the data first? Search timeline event notes. Искать записи событий timeline. + + Hide or show event categories. + Показ или скрытие категорий событий. + Disable profiling Отключить профилирование @@ -29332,7 +30781,19 @@ Do you want to save the data first? - QmlProfiler::QmlProfilerBaseModel + QmlProfiler::QmlProfilerDataModel + + <bytecode> + <байтовый код> + + + anonymous function + анонимная функция + + + GUI Thread + Поток GUI + µs мкс @@ -29346,27 +30807,8 @@ Do you want to save the data first? с - - QmlProfiler::QmlProfilerDataModel - - <bytecode> - <байтовый код> - - - anonymous function - анонимная функция - - - GUI Thread - Поток GUI - - QmlProfiler::QmlProfilerModelManager - - Unexpected complete signal in data model. - Неожиданный сигнал complete в модели данных. - Could not open %1 for writing. Не удалось открыть %1 для записи. @@ -29383,6 +30825,10 @@ Do you want to save the data first? Loading Trace Data Загрузка данных трассировки + + Trying to set unknown state in events list. + Попытка установить неизвестное состояние в списке событий. + QmlProfiler::QmlProfilerRunControl @@ -29397,9 +30843,12 @@ Do you want to save the data first? Не удалось подключиться к внутрипроцессному отладчику QML: %1 + + + QmlProfilerRunConfiguration - QML Profiler - Профайлер QML + QML Profiler Settings + Настройки профайлера QML @@ -29460,6 +30909,10 @@ Do you want to save the data first? System Environment Системная среда + + Kit Environment + Среда комплекта + QmlProjectManager::QmlProjectFileFormat @@ -29493,21 +30946,6 @@ Do you want to save the data first? - - QmlWarningDialog - - Warning - Предупреждение - - - This QML file contains features which are not supported by Qt Quick Designer - Этот файл QML содержит свойства, не поддерживаемые Qt Quick Designer - - - Warn about unsupported features - Предупреждать о неподдерживаемых функциях - - Qnx::Internal::QnxAbstractRunSupport @@ -30237,10 +31675,6 @@ cannot be found in the path. Cannot Copy Project Не удалось скопировать проект - - Failed to Open Project - Не удалось открыть проект - QtSupport::Internal::QtKitConfigWidget @@ -30275,6 +31709,18 @@ cannot be found in the path. Manual Особые + + Name + Название + + + qmake Location + Размещение qmake + + + Type + Тип + Do you want to remove all invalid Qt Versions?<br><ul><li>%1</li></ul><br>will be removed. Обнаружены неверные профили Qt:<br><ul><li>%1</li></ul><br>Удалить? @@ -30368,8 +31814,8 @@ cannot be found in the path. Полный путь на хосте к каталогу bin профиля Qt, используемого в текущем проекте. - Full path to the target bin directory of the current project's Qt version. You probably want %1 instead. - Полный путь на цели к каталогу bin профиля Qt, используемого в текущем проекте. Возможно, нужен %1. + Full path to the target bin directory of the current project's Qt version.<br>You probably want %1 instead. + Полный путь каталогу bin профиля Qt, используемого в текущем проекте.<br>Возможно, нужен %1. @@ -30389,14 +31835,6 @@ cannot be found in the path. QtSupport::Internal::QtVersionManager - - Name - Название - - - qmake Location - Размещение qmake - Remove Удалить @@ -31909,6 +33347,13 @@ In addition, device connectivity will be tested. Определяет, обновляется ли текущее значение ползунка во время перемещения пользователем, или только при отпускании кнопки. + + SourceToolAction + + Change Source URL... + Изменить исходный URL... + + SplitViewSpecifics @@ -33025,6 +34470,39 @@ Specifies how backspace interacts with indentation. Erase background. Убрать фон. + + Underline: + Подчёркивание: + + + No Underline + Подчёркивание: отсутствует + Отсутствует + + + Single Underline + Сплошное + + + Wave Underline + Волнистое + + + Dot Underline + Пунктирное + + + Dash Underline + Штриховое + + + Dash-Dot Underline + Штрихпунктирное + + + Dash-Dot-Dot Underline + Штрихпунктирное с двумя точками + TextEditor::Internal::CountingLabel @@ -33661,7 +35139,7 @@ Influences the indentation of continuation lines. Ctrl+E, F2 - Jump To File Under Cursor + Jump to File Under Cursor Перейти к файлу под курсором @@ -34332,6 +35810,10 @@ Applied to text, if no other rules matching. Applied to removed lines in differences (in diff editor). Применяется к удаляемым строкам при сравнении (в редакторе изменений). + + Zoom: %1% + Масштаб: %1% + Disabled Code Отключённый код @@ -34458,6 +35940,38 @@ Will not be applied to whitespace in comments and strings. Applied to lines describing changes in VCS log. Применяется к строкам, описывающим изменения в журнале VCS. + + Error + Ошибка + + + Underline color of error diagnostics. + Цвет подчёркивания ошибок. + + + Error Context + Контекст ошибки + + + Underline color of the contexts of error diagnostics. + Цвет подчёркивания контекстов ошибок. + + + Warning + Предупреждение + + + Underline color of warning diagnostics. + Цвет подчёркивания предупреждений. + + + Warning Context + Контекст предупреждения + + + Underline color of the contexts of warning diagnostics. + Цвет подчёркивания контекстов предупреждений. + Behavior Поведение @@ -34622,6 +36136,20 @@ Will not be applied to whitespace in comments and strings. Цвет стиля + + TextTool + + Text Tool + Текст + + + + TextToolAction + + Edit Text + Изменить текст + + Todo::Internal::KeywordDialog @@ -34681,12 +36209,16 @@ Will not be applied to whitespace in comments and strings. Scan the whole active project - Искать во всём текущем проекте + Искать во всём активном проекте Scan only the currently edited document Искать только в текущем документе + + Scan the current subproject + Искать в текущем подпроекте + Todo::Internal::OptionsPage @@ -34732,6 +36264,14 @@ Will not be applied to whitespace in comments and strings. Scan the whole active project. Искать во всём текущем проекте. + + Subproject + Подпроект + + + Scan the current subproject. + Искать в текущем подпроекте. + Todo::Internal::TodoProjectSettingsWidget @@ -35193,6 +36733,13 @@ Will not be applied to whitespace in comments and strings. Размещение + + Utils::HostOsInfo + + Cannot create OpenGL context. + Не удалось создать контекст OpenGL. + + Utils::LinearProgressWidget @@ -36015,8 +37562,8 @@ Will not be applied to whitespace in comments and strings. Valgrind::Internal - %1 in %2 - %1 в %2 + %1%2 + %1%2 in %1 @@ -36577,49 +38124,17 @@ When a problem is detected, the application is interrupted and can be debugged.< Valgrind::XmlProtocol::ErrorListModel - - No errors found - Ошибки не найдены - - - What - Что - Location Размещение - File - Файл + Issue + Проблема - Line - Строка - - - Unique - Уникальный - - - Thread ID - ID потока - - - Kind - Вид - - - Leaked Blocks - Потерянные блоки - - - Leaked Bytes - Потерянные байты - - - Helgrind Thread ID - ID потока Helgrind + %1 in function %2 + %1 в функции %2 @@ -37488,6 +39003,482 @@ should a repository require SSH-authentication (see documentation on SSH and the Поиск по примерам... + + qmt::DiagramController + + Change + Изменение + + + Add Object + Добавить объект + + + Remove Object + Удалить объект + + + Cut + Вырезать + + + Paste + Вставить + + + Delete + Удалить + + + + qmt::DiagramSceneController + + Create Dependency + Создание зависимости + + + Create Inheritance + Создание наследования + + + Create Association + Создание ассоциации + + + New Package + Новый пакет + + + New Component + Новый компонент + + + New Class + Новый класс + + + New Item + Новый элемент + + + New %1 + Новый %1 + + + Drop Element + Удаление элемента + + + Add Element + Добавление элемента + + + + qmt::DocumentController + + New Package + Создать пакет + + + New Class + Создать класс + + + New Component + Создать компонент + + + New Diagram + Создать диаграмму + + + + qmt::ModelController + + Change Object + Изменение объекта + + + Change Relation + Изменение отношения + + + Move Object + Перемещение объекта + + + Move Relation + Перемещение отношения + + + Add Object + Добавить объект + + + Delete Object + Удалить объект + + + Add Relation + Добавить отношение + + + Delete Relation + Удалить отношение + + + Cut + Вырезать + + + Paste + Вставить + + + Delete + Удалить + + + + qmt::ModelTreeView + + Show Definition + Показать определение + + + Open Diagram + Открыть диаграмму + + + Delete + Удалить + + + + qmt::ProjectController + + Missing file name. + Отсутствует имя файла. + + + Project is modified. + Проект изменён. + + + Model + Модель + + + + qmt::PropertiesView::MView + + Yes + Да + + + No + Нет + + + Model + Модель + + + Models + Модели + + + Package + Пакет + + + Packages + Пакеты + + + Class + Класс + + + Classes + Классы + + + Component + Компонент + + + Components + Компоненты + + + Diagram + Диаграмма + + + Diagrams + Диаграммы + + + Canvas Diagram + Диаграмма холста + + + Canvas Diagrams + Диаграммы холста + + + Item + Элемент + + + Items + Элементы + + + End A: %1 + Конец А: %1 + + + End B: %1 + Конец Б: %1 + + + Dependency + Зависимость + + + Dependencies + Зависимости + + + Inheritance + Наследование + + + Inheritances + Наследования + + + Association + Ассоциация + + + Associations + Ассоциации + + + Navigable + Управляемый + + + Aggregation + Агрегация + + + Composition + Композиция + + + Stereotypes: + Стереотипы: + + + Reverese engineered: + Реконструировано: + + + Name: + Имя: + + + Children: + Потомки: + + + Relations: + Отношения: + + + Namespace: + Пространство имён: + + + Template: + Шаблон: + + + Members: + Члены: + + + Elements: + Элементы: + + + Variety: + Множество: + + + Direction: + Направление: + + + Derived class: %1 + Производный класс: %1 + + + Base class: %1 + Базовый класс: %1 + + + Role: + Роль: + + + Cardinality: + Число элементов: + + + Relationship: + Связь: + + + Position and size: + Положение и размер: + + + Auto sized + Авторазмер + + + Color: + Цвет: + + + Normal + Обычный + + + Lighter + Светлее + + + Darker + Темнее + + + Soften + Мягче + + + Outline + Обведённым + + + Emphasized + Выделенным + + + Smart + Умно + + + None + Никак + + + Label + Метка + + + Decoration + Декорация + + + Icon + Значок + + + Stereotype display: + Отображать стереотип: + + + Depth: + Глубина: + + + Template display: + Отображать шаблон: + + + Show members + Показывать членов + + + Plain shape + Простая фигура + + + Shape: + Фигура: + + + Auto width + Автоширина + + + Box + Коробкой + + + Angle Brackets + Угловыми скобками + + + Annotation + Аннотация + + + Annotations + Аннотации + + + Title + Заголовок + + + Subtitle + Подзаголовок + + + Footnote + Сноска + + + Boundary + Граница + + + Boundaries + Границы + + + <font color=red>Invalid syntax!</font> + <font color=red>Неверный синтаксис!</font> + + + Multi-Selection + Множественное выделение + + + + qmt::TreeModel + + [unnamed] + [без имени] + + text diff --git a/src/app/main.cpp b/src/app/main.cpp index 1cfccf42cf4..ef7d9982180 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -159,6 +159,28 @@ static inline int askMsgSendFailed() QMessageBox::Retry); } +static const char *setHighDpiEnvironmentVariable() +{ + const char* envVarName = 0; + static const char ENV_VAR_QT_DEVICE_PIXEL_RATIO[] = "QT_DEVICE_PIXEL_RATIO"; +#if (QT_VERSION < QT_VERSION_CHECK(5, 6, 0)) + if (Utils::HostOsInfo().isWindowsHost() + && !qEnvironmentVariableIsSet(ENV_VAR_QT_DEVICE_PIXEL_RATIO)) { + envVarName = ENV_VAR_QT_DEVICE_PIXEL_RATIO; + qputenv(envVarName, "auto"); + } +#else + if (Utils::HostOsInfo().isWindowsHost() + && !qEnvironmentVariableIsSet(ENV_VAR_QT_DEVICE_PIXEL_RATIO) // legacy in 5.6, but still functional + && !qEnvironmentVariableIsSet("QT_AUTO_SCREEN_SCALE_FACTOR") + && !qEnvironmentVariableIsSet("QT_SCALE_FACTOR") + && !qEnvironmentVariableIsSet("QT_SCREEN_SCALE_FACTORS")) { + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + } +#endif // < Qt 5.6 + return envVarName; +} + // taken from utils/fileutils.cpp. We can not use utils here since that depends app_version.h. static bool copyRecursively(const QString &srcFilePath, const QString &tgtFilePath) @@ -280,12 +302,7 @@ static const char *SHARE_PATH = int main(int argc, char **argv) { -#if (QT_VERSION < QT_VERSION_CHECK(5, 6, 0)) - if (Utils::HostOsInfo().isWindowsHost() - && !qEnvironmentVariableIsSet("QT_DEVICE_PIXEL_RATIO")) { - qputenv("QT_DEVICE_PIXEL_RATIO", "auto"); - } -#endif // < Qt 5.6 + const char *highDpiEnvironmentVariable = setHighDpiEnvironmentVariable(); QLoggingCategory::setFilterRules(QLatin1String("qtc.*.debug=false")); #ifdef Q_OS_MAC @@ -299,6 +316,9 @@ int main(int argc, char **argv) SharedTools::QtSingleApplication app((QLatin1String(appNameC)), argc, argv); + if (highDpiEnvironmentVariable) + qunsetenv(highDpiEnvironmentVariable); + if (Utils::HostOsInfo().isWindowsHost() && !qFuzzyCompare(qApp->devicePixelRatio(), 1.0) && QApplication::style()->objectName().startsWith( diff --git a/src/libs/clangbackendipc/filecontainer.cpp b/src/libs/clangbackendipc/filecontainer.cpp index 9614b0ce481..47337cdc725 100644 --- a/src/libs/clangbackendipc/filecontainer.cpp +++ b/src/libs/clangbackendipc/filecontainer.cpp @@ -54,9 +54,11 @@ FileContainer::FileContainer(const Utf8String &filePath, FileContainer::FileContainer(const Utf8String &filePath, const Utf8String &projectPartId, + const Utf8StringVector &fileArguments, quint32 documentRevision) : filePath_(filePath), projectPartId_(projectPartId), + fileArguments_(fileArguments), documentRevision_(documentRevision), hasUnsavedFileContent_(false) { @@ -72,6 +74,11 @@ const Utf8String &FileContainer::projectPartId() const return projectPartId_; } +const Utf8StringVector &FileContainer::fileArguments() const +{ + return fileArguments_; +} + const Utf8String &FileContainer::unsavedFileContent() const { return unsavedFileContent_; @@ -91,6 +98,7 @@ QDataStream &operator<<(QDataStream &out, const FileContainer &container) { out << container.filePath_; out << container.projectPartId_; + out << container.fileArguments_; out << container.unsavedFileContent_; out << container.documentRevision_; out << container.hasUnsavedFileContent_; @@ -102,6 +110,7 @@ QDataStream &operator>>(QDataStream &in, FileContainer &container) { in >> container.filePath_; in >> container.projectPartId_; + in >> container.fileArguments_; in >> container.unsavedFileContent_; in >> container.documentRevision_; in >> container.hasUnsavedFileContent_; @@ -127,6 +136,7 @@ QDebug operator<<(QDebug debug, const FileContainer &container) debug.nospace() << "FileContainer(" << container.filePath() << ", " << container.projectPartId() << ", " + << container.fileArguments() << ", " << container.documentRevision(); if (container.hasUnsavedFileContent()) { @@ -147,6 +157,7 @@ void PrintTo(const FileContainer &container, ::std::ostream* os) *os << "FileContainer(" << container.filePath().constData() << ", " << container.projectPartId().constData() << ", " + << container.fileArguments().constData() << ", " << container.documentRevision(); if (container.hasUnsavedFileContent()) diff --git a/src/libs/clangbackendipc/filecontainer.h b/src/libs/clangbackendipc/filecontainer.h index 7039dc7653b..4ca60b9b2bc 100644 --- a/src/libs/clangbackendipc/filecontainer.h +++ b/src/libs/clangbackendipc/filecontainer.h @@ -34,6 +34,7 @@ #include #include +#include #include @@ -54,10 +55,12 @@ public: quint32 documentRevision = 0); FileContainer(const Utf8String &filePath, const Utf8String &projectPartId, + const Utf8StringVector &fileArguments, quint32 documentRevision); const Utf8String &filePath() const; const Utf8String &projectPartId() const; + const Utf8StringVector &fileArguments() const; const Utf8String &unsavedFileContent() const; bool hasUnsavedFileContent() const; quint32 documentRevision() const; @@ -65,6 +68,7 @@ public: private: Utf8String filePath_; Utf8String projectPartId_; + Utf8StringVector fileArguments_; Utf8String unsavedFileContent_; quint32 documentRevision_; bool hasUnsavedFileContent_ = false; diff --git a/src/libs/cplusplus/LookupContext.cpp b/src/libs/cplusplus/LookupContext.cpp index 0364b59c725..401d54f48b3 100644 --- a/src/libs/cplusplus/LookupContext.cpp +++ b/src/libs/cplusplus/LookupContext.cpp @@ -1549,6 +1549,7 @@ CreateBindings::CreateBindings(Document::Ptr thisDocument, const Snapshot &snaps : _snapshot(snapshot) , _control(QSharedPointer(new Control)) , _expandTemplates(false) + , _depth(0) { _globalNamespace = allocLookupScope(/*parent = */ 0, /*name = */ 0); _currentLookupScope = _globalNamespace; @@ -1978,8 +1979,13 @@ void CreateBindings::initializeSubst(Clone &cloner, { const unsigned argumentCountOfSpecialization = specialization->templateParameterCount(); + if (_depth > 15) + return; + + ++_depth; for (unsigned i = 0; i < argumentCountOfSpecialization; ++i) resolveTemplateArgument(cloner, subst, origin, specialization, instantiation, i); + --_depth; } } // namespace CPlusPlus diff --git a/src/libs/cplusplus/LookupContext.h b/src/libs/cplusplus/LookupContext.h index 8d4016dbbbb..8410c9000b7 100644 --- a/src/libs/cplusplus/LookupContext.h +++ b/src/libs/cplusplus/LookupContext.h @@ -209,6 +209,7 @@ private: LookupScope *_globalNamespace; LookupScope *_currentLookupScope; bool _expandTemplates; + int _depth; }; class CPLUSPLUS_EXPORT LookupContext diff --git a/src/libs/timeline/timelineselectionrenderpass.cpp b/src/libs/timeline/timelineselectionrenderpass.cpp index 7c723eb5cfb..c5e63a5243f 100644 --- a/src/libs/timeline/timelineselectionrenderpass.cpp +++ b/src/libs/timeline/timelineselectionrenderpass.cpp @@ -102,9 +102,12 @@ TimelineRenderPass::State *TimelineSelectionRenderPass::update( top = TimelineModel::defaultRowHeight() * (row + 1) - height; } - qint64 startTime = model->startTime(selectedItem); - qint64 left = qMax(startTime - parentState->start(), (qint64)0); - qint64 width = qMin(parentState->end() - startTime, model->duration(selectedItem)); + qint64 startTime = qBound(parentState->start(), model->startTime(selectedItem), + parentState->end()); + qint64 endTime = qBound(parentState->start(), model->endTime(selectedItem), + parentState->end()); + qint64 left = startTime - parentState->start(); + qint64 width = endTime - startTime; // Construct from upper left and lower right for better precision. When constructing from // left and width the error on the left border is inherited by the right border. Like this diff --git a/src/libs/utils/fadingindicator.cpp b/src/libs/utils/fadingindicator.cpp index 9b00ce51df0..75e30e5f7c4 100644 --- a/src/libs/utils/fadingindicator.cpp +++ b/src/libs/utils/fadingindicator.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -130,14 +131,20 @@ namespace FadingIndicator { void showText(QWidget *parent, const QString &text, TextSize size) { - auto indicator = new Internal::FadingIndicatorPrivate(parent, size); + static QPointer indicator; + if (indicator) + delete indicator; + indicator = new Internal::FadingIndicatorPrivate(parent, size); indicator->setText(text); indicator->run(2500); // deletes itself } void showPixmap(QWidget *parent, const QString &pixmap) { - auto indicator = new Internal::FadingIndicatorPrivate(parent, LargeText); + static QPointer indicator; + if (indicator) + delete indicator; + indicator = new Internal::FadingIndicatorPrivate(parent, LargeText); indicator->setPixmap(pixmap); indicator->run(300); // deletes itself } diff --git a/src/libs/utils/fileutils_mac.mm b/src/libs/utils/fileutils_mac.mm index 27c0f127b87..dc31c80f884 100644 --- a/src/libs/utils/fileutils_mac.mm +++ b/src/libs/utils/fileutils_mac.mm @@ -30,7 +30,6 @@ #include "fileutils_mac.h" -#include "autoreleasepool.h" #include "qtcassert.h" #include @@ -44,45 +43,46 @@ namespace Internal { QUrl filePathUrl(const QUrl &url) { - Utils::AutoreleasePool pool; Q_UNUSED(pool) QUrl ret = url; - NSURL *nsurl = url.toNSURL(); - if ([nsurl isFileReferenceURL]) - ret = QUrl::fromNSURL([nsurl filePathURL]); + @autoreleasepool { + NSURL *nsurl = url.toNSURL(); + if ([nsurl isFileReferenceURL]) + ret = QUrl::fromNSURL([nsurl filePathURL]); + } return ret; } QString normalizePathName(const QString &filePath) { - AutoreleasePool pool; Q_UNUSED(pool) - - // NSURL getResourceValue returns values based on the cleaned path so we need to work on that. - // It also returns the disk name for "/" and "/.." and errors on "" and relative paths, - // so avoid that - - // we cannot know the normalized name for relative paths - if (QFileInfo(filePath).isRelative()) - return filePath; - QString result; - QString path = QDir::cleanPath(filePath); - // avoid empty paths and paths like "/../foo" or "/.." - if (path.isEmpty() || path.contains(QLatin1String("/../")) || path.endsWith(QLatin1String("/.."))) - return filePath; + @autoreleasepool { + // NSURL getResourceValue returns values based on the cleaned path so we need to work on that. + // It also returns the disk name for "/" and "/.." and errors on "" and relative paths, + // so avoid that - while (path != QLatin1String("/") /*be defensive->*/&& path != QLatin1String(".") && !path.isEmpty()) { - QFileInfo info(path); - NSURL *nsurl = [NSURL fileURLWithPath:path.toNSString()]; - NSString *out; - QString component; - if ([nsurl getResourceValue:(NSString **)&out forKey:NSURLNameKey error:nil]) - component = QString::fromNSString(out); - else // e.g. if the full path does not exist - component = info.fileName(); - result.prepend(QLatin1Char('/') + component); - path = info.path(); + // we cannot know the normalized name for relative paths + if (QFileInfo(filePath).isRelative()) + return filePath; + + QString path = QDir::cleanPath(filePath); + // avoid empty paths and paths like "/../foo" or "/.." + if (path.isEmpty() || path.contains(QLatin1String("/../")) || path.endsWith(QLatin1String("/.."))) + return filePath; + + while (path != QLatin1String("/") /*be defensive->*/&& path != QLatin1String(".") && !path.isEmpty()) { + QFileInfo info(path); + NSURL *nsurl = [NSURL fileURLWithPath:path.toNSString()]; + NSString *out; + QString component; + if ([nsurl getResourceValue:(NSString **)&out forKey:NSURLNameKey error:nil]) + component = QString::fromNSString(out); + else // e.g. if the full path does not exist + component = info.fileName(); + result.prepend(QLatin1Char('/') + component); + path = info.path(); + } + QTC_ASSERT(path == QLatin1String("/"), return filePath); } - QTC_ASSERT(path == QLatin1String("/"), return filePath); return result; } diff --git a/src/libs/utils/mimetypes/mimedatabase.cpp b/src/libs/utils/mimetypes/mimedatabase.cpp index be018edcc8e..73bd2576903 100644 --- a/src/libs/utils/mimetypes/mimedatabase.cpp +++ b/src/libs/utils/mimetypes/mimedatabase.cpp @@ -230,13 +230,19 @@ bool MimeDatabasePrivate::inherits(const QString &mime, const QString &parent) const QString resolvedParent = provider()->resolveAlias(parent); //Q_ASSERT(provider()->resolveAlias(mime) == mime); QStack toCheck; + QSet seen; // avoid endless loop on bogus mime data toCheck.push(mime); + seen.insert(mime); while (!toCheck.isEmpty()) { const QString current = toCheck.pop(); if (current == resolvedParent) return true; - foreach (const QString &par, provider()->parents(current)) - toCheck.push(par); + foreach (const QString &par, provider()->parents(current)) { + int seenSize = seen.size(); + seen.insert(par); + if (seen.size() != seenSize) // haven't seen before, so add + toCheck.push(par); + } } return false; } diff --git a/src/libs/utils/theme/theme.cpp b/src/libs/utils/theme/theme.cpp index 41e7cb5a7bd..bbfd5c709d7 100644 --- a/src/libs/utils/theme/theme.cpp +++ b/src/libs/utils/theme/theme.cpp @@ -66,11 +66,11 @@ void setCreatorTheme(Theme *theme) m_creatorTheme = theme; } -Theme::Theme(const QString &name, QObject *parent) +Theme::Theme(const QString &id, QObject *parent) : QObject(parent) , d(new ThemePrivate) { - d->name = name; + d->id = id; } Theme::~Theme() @@ -88,6 +88,11 @@ QStringList Theme::preferredStyles() const return d->preferredStyles; } +QString Theme::id() const +{ + return d->id; +} + bool Theme::flag(Theme::Flag f) const { return d->flags[f]; @@ -130,14 +135,14 @@ QString Theme::filePath() const return d->fileName; } -QString Theme::name() const +QString Theme::displayName() const { - return d->name; + return d->displayName; } -void Theme::setName(const QString &name) +void Theme::setDisplayName(const QString &name) { - d->name = name; + d->displayName = name; } QVariantHash Theme::values() const @@ -185,7 +190,7 @@ void Theme::writeSettings(const QString &filename) const const QMetaObject &m = *metaObject(); { - settings.setValue(QLatin1String("ThemeName"), d->name); + settings.setValue(QLatin1String("ThemeName"), d->displayName); settings.setValue(QLatin1String("PreferredStyles"), d->preferredStyles); } { @@ -264,7 +269,7 @@ void Theme::readSettings(QSettings &settings) const QMetaObject &m = *metaObject(); { - d->name = settings.value(QLatin1String("ThemeName"), QLatin1String("unnamed")).toString(); + d->displayName = settings.value(QLatin1String("ThemeName"), QLatin1String("unnamed")).toString(); d->preferredStyles = settings.value(QLatin1String("PreferredStyles")).toStringList(); d->preferredStyles.removeAll(QLatin1String("")); } diff --git a/src/libs/utils/theme/theme.h b/src/libs/utils/theme/theme.h index 994822ca822..bd398847622 100644 --- a/src/libs/utils/theme/theme.h +++ b/src/libs/utils/theme/theme.h @@ -54,7 +54,7 @@ class QTCREATOR_UTILS_EXPORT Theme : public QObject Q_ENUMS(WidgetStyle) public: - Theme(const QString &name, QObject *parent = 0); + Theme(const QString &id, QObject *parent = 0); ~Theme(); enum Color { @@ -265,9 +265,10 @@ public: QPalette palette() const; QStringList preferredStyles() const; + QString id() const; QString filePath() const; - QString name() const; - void setName(const QString &name); + QString displayName() const; + void setDisplayName(const QString &displayName); QVariantHash values() const; diff --git a/src/libs/utils/theme/theme_p.h b/src/libs/utils/theme/theme_p.h index 22dcc44fb71..ea8b4d3d477 100644 --- a/src/libs/utils/theme/theme_p.h +++ b/src/libs/utils/theme/theme_p.h @@ -44,8 +44,9 @@ class QTCREATOR_UTILS_EXPORT ThemePrivate public: ThemePrivate(); + QString id; QString fileName; - QString name; + QString displayName; QStringList preferredStyles; QVector > colors; QVector imageFiles; diff --git a/src/libs/utils/utils-lib.pri b/src/libs/utils/utils-lib.pri index 707e851f1b2..9c6ea3a5fde 100644 --- a/src/libs/utils/utils-lib.pri +++ b/src/libs/utils/utils-lib.pri @@ -210,7 +210,7 @@ FORMS += $$PWD/filewizardpage.ui \ RESOURCES += $$PWD/utils.qrc osx { - HEADERS += $$PWD/autoreleasepool.h \ + HEADERS += \ $$PWD/fileutils_mac.h OBJECTIVE_SOURCES += \ $$PWD/fileutils_mac.mm diff --git a/src/plugins/baremetal/baremetalconstants.h b/src/plugins/baremetal/baremetalconstants.h index 519988c8ddb..a907b898fd3 100644 --- a/src/plugins/baremetal/baremetalconstants.h +++ b/src/plugins/baremetal/baremetalconstants.h @@ -41,7 +41,7 @@ const char ACTION_ID[] = "BareMetal.Action"; const char MENU_ID[] = "BareMetal.Menu"; const char BAREMETAL_SETTINGS_CATEGORY[] = "ZZ.BareMetal"; -const char BAREMETAL_SETTINGS_TR_CATEGORY[] = QT_TRANSLATE_NOOP("BareMetal", "BareMetal"); +const char BAREMETAL_SETTINGS_TR_CATEGORY[] = QT_TRANSLATE_NOOP("BareMetal", "Bare Metal"); const char BAREMETAL_SETTINGS_CATEGORY_ICON[] = ":/baremetal/images/QtBareMetal.png"; const char GDB_PROVIDERS_SETTINGS_ID[] = "AA.BareMetal.GdbServerProvidersOptions"; diff --git a/src/plugins/clangcodemodel/clangbackendipcintegration.cpp b/src/plugins/clangcodemodel/clangbackendipcintegration.cpp index ec335f39c6b..24c3415b0f3 100644 --- a/src/plugins/clangcodemodel/clangbackendipcintegration.cpp +++ b/src/plugins/clangcodemodel/clangbackendipcintegration.cpp @@ -306,12 +306,44 @@ void IpcCommunicator::initializeBackend() initializeBackendWithCurrentData(); } -void IpcCommunicator::registerEmptyProjectForProjectLessFiles() +static QStringList projectPartOptions(const CppTools::ProjectPart::Ptr &projectPart) +{ + QStringList options = ClangCodeModel::Utils::createClangOptions(projectPart, + CppTools::ProjectFile::Unclassified); // No language option + if (PchInfo::Ptr pchInfo = PchManager::instance()->pchInfo(projectPart)) + options += ClangCodeModel::Utils::createPCHInclusionOptions(pchInfo->fileName()); + + return options; +} + +static ClangBackEnd::ProjectPartContainer toProjectPartContainer( + const CppTools::ProjectPart::Ptr &projectPart) +{ + const QStringList options = projectPartOptions(projectPart); + + return ClangBackEnd::ProjectPartContainer(projectPart->id(), Utf8StringVector(options)); +} + +static QVector toProjectPartContainers( + const QList projectParts) +{ + QVector projectPartContainers; + projectPartContainers.reserve(projectParts.size()); + + foreach (const CppTools::ProjectPart::Ptr &projectPart, projectParts) + projectPartContainers << toProjectPartContainer(projectPart); + + return projectPartContainers; +} + +void IpcCommunicator::registerFallbackProjectPart() { QTC_CHECK(m_connection.isConnected()); - registerProjectPartsForEditor({ClangBackEnd::ProjectPartContainer( - Utf8String(), - Utf8StringVector())}); + + const auto projectPart = CppTools::CppModelManager::instance()->fallbackProjectPart(); + const auto projectPartContainer = toProjectPartContainer(projectPart); + + registerProjectPartsForEditor({projectPartContainer}); } void IpcCommunicator::registerCurrentProjectParts() @@ -343,49 +375,12 @@ void IpcCommunicator::registerCurrentCodeModelUiHeaders() updateUnsavedFile(es->fileName(), es->contents(), es->revision()); } -static QStringList projectPartMessageLine(const CppTools::ProjectPart::Ptr &projectPart) -{ - QStringList options = ClangCodeModel::Utils::createClangOptions(projectPart, - CppTools::ProjectFile::Unclassified); // No language option - if (PchInfo::Ptr pchInfo = PchManager::instance()->pchInfo(projectPart)) - options += ClangCodeModel::Utils::createPCHInclusionOptions(pchInfo->fileName()); - - return options; -} - -static ClangBackEnd::ProjectPartContainer toProjectPartContainer( - const CppTools::ProjectPart::Ptr &projectPart) -{ - const QStringList arguments = projectPartMessageLine(projectPart); - return ClangBackEnd::ProjectPartContainer(projectPart->id(), Utf8StringVector(arguments)); -} - -static QVector toProjectPartContainers( - const QList projectParts) -{ - QVector projectPartContainers; - projectPartContainers.reserve(projectParts.size()); - foreach (const CppTools::ProjectPart::Ptr &projectPart, projectParts) - projectPartContainers << toProjectPartContainer(projectPart); - return projectPartContainers; -} - void IpcCommunicator::registerProjectsParts(const QList projectParts) { const auto projectPartContainers = toProjectPartContainers(projectParts); registerProjectPartsForEditor(projectPartContainers); } -void IpcCommunicator::registerTranslationUnit(TextEditor::TextDocument *document) -{ - const QString filePath = document->filePath().toString(); - const QString projectPartId = Utils::projectPartIdForFile(filePath); - - registerTranslationUnitsForEditor({{Utf8String(filePath), - Utf8String(projectPartId), - uint(document->document()->revision())}}); -} - void IpcCommunicator::updateTranslationUnitFromCppEditorDocument(const QString &filePath) { const auto document = CppTools::CppModelManager::instance()->cppEditorDocument(filePath); @@ -426,25 +421,6 @@ void setLastSentDocumentRevision(const QString &filePath, } } -void IpcCommunicator::registerTranslationUnit(const QString &filePath, - const QByteArray &contents, - uint documentRevision) -{ - const QString projectPartId = Utils::projectPartIdForFile(filePath); - - if (documentHasChanged(filePath)) { - const bool hasUnsavedContent = true; - - registerTranslationUnitsForEditor({{filePath, - projectPartId, - Utf8String::fromByteArray(contents), - hasUnsavedContent, - documentRevision}}); - - setLastSentDocumentRevision(filePath, documentRevision); - } -} - void IpcCommunicator::updateTranslationUnit(const QString &filePath, const QByteArray &contents, uint documentRevision) @@ -495,6 +471,7 @@ void IpcCommunicator::requestDiagnostics(Core::IDocument *document) requestDiagnostics(FileContainer(filePath, projectPartId, + Utf8StringVector(), textDocument->document()->revision())); } @@ -547,7 +524,7 @@ void IpcCommunicator::onCoreAboutToClose() void IpcCommunicator::initializeBackendWithCurrentData() { - registerEmptyProjectForProjectLessFiles(); + registerFallbackProjectPart(); registerCurrentProjectParts(); registerCurrentCppEditorDocuments(); registerCurrentCodeModelUiHeaders(); diff --git a/src/plugins/clangcodemodel/clangbackendipcintegration.h b/src/plugins/clangcodemodel/clangbackendipcintegration.h index eb08e7aaa9e..030976bfbe9 100644 --- a/src/plugins/clangcodemodel/clangbackendipcintegration.h +++ b/src/plugins/clangcodemodel/clangbackendipcintegration.h @@ -133,8 +133,6 @@ public: void registerProjectsParts(const QList projectParts); - void registerTranslationUnit(TextEditor::TextDocument *document); - void registerTranslationUnit(const QString &filePath, const QByteArray &contents, uint documentRevision); void updateTranslationUnitIfNotCurrentDocument(Core::IDocument *document); void updateTranslationUnit(Core::IDocument *document); void updateUnsavedFile(Core::IDocument *document); @@ -146,6 +144,8 @@ public: void requestDiagnostics(Core::IDocument *document); void updateChangeContentStartPosition(const QString &filePath, int position); + void registerFallbackProjectPart(); + public: // for tests IpcSenderInterface *setIpcSender(IpcSenderInterface *ipcSender); void killBackendProcess(); @@ -158,7 +158,6 @@ private: void initializeBackend(); void initializeBackendWithCurrentData(); - void registerEmptyProjectForProjectLessFiles(); void registerCurrentProjectParts(); void registerCurrentCppEditorDocuments(); void registerCurrentCodeModelUiHeaders(); diff --git a/src/plugins/clangcodemodel/clangcodemodelplugin.cpp b/src/plugins/clangcodemodel/clangcodemodelplugin.cpp index b69366ee4c2..928b034387f 100644 --- a/src/plugins/clangcodemodel/clangcodemodelplugin.cpp +++ b/src/plugins/clangcodemodel/clangcodemodelplugin.cpp @@ -58,6 +58,11 @@ static void initializeTextMarks() Utils::Theme::ClangCodeModel_Error_TextMarkColor); } +ClangCodeModelPlugin::ClangCodeModelPlugin() +{ + qRegisterMetaType(); +} + bool ClangCodeModelPlugin::initialize(const QStringList &arguments, QString *errorMessage) { Q_UNUSED(arguments) diff --git a/src/plugins/clangcodemodel/clangcodemodelplugin.h b/src/plugins/clangcodemodel/clangcodemodelplugin.h index 6a767d5b710..39d4ba6633c 100644 --- a/src/plugins/clangcodemodel/clangcodemodelplugin.h +++ b/src/plugins/clangcodemodel/clangcodemodelplugin.h @@ -50,6 +50,8 @@ class ClangCodeModelPlugin: public ExtensionSystem::IPlugin Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "ClangCodeModel.json") public: + ClangCodeModelPlugin(); + bool initialize(const QStringList &arguments, QString *errorMessage); void extensionsInitialized(); diff --git a/src/plugins/clangcodemodel/clangeditordocumentparser.cpp b/src/plugins/clangcodemodel/clangeditordocumentparser.cpp index 43a78680176..caeefd2267d 100644 --- a/src/plugins/clangcodemodel/clangeditordocumentparser.cpp +++ b/src/plugins/clangcodemodel/clangeditordocumentparser.cpp @@ -99,6 +99,7 @@ void ClangEditorDocumentParser::updateHelper(const BaseEditorDocumentParser::InM State state_ = state(); state_.projectPart = determineProjectPart(filePath(), configuration(), state_); setState(state_); + emit projectPartDetermined(state_.projectPart); // Determine message line arguments const QStringList options = createOptions(filePath(), state_.projectPart, true); diff --git a/src/plugins/clangcodemodel/clangeditordocumentparser.h b/src/plugins/clangcodemodel/clangeditordocumentparser.h index 4784d51672c..ef207749c27 100644 --- a/src/plugins/clangcodemodel/clangeditordocumentparser.h +++ b/src/plugins/clangcodemodel/clangeditordocumentparser.h @@ -50,6 +50,9 @@ public: QList ifdefedOutBlocks() const; SemanticMarker::Ptr semanticMarker() const; +signals: + void projectPartDetermined(CppTools::ProjectPart::Ptr projectPart); + private: void updateHelper(const BaseEditorDocumentParser::InMemoryInfo &info) override; diff --git a/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp b/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp index fe511ee560a..341f541255c 100644 --- a/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp +++ b/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp @@ -41,7 +41,9 @@ #include #include -#include +#include +#include +#include #include #include @@ -87,6 +89,9 @@ ClangEditorDocumentProcessor::ClangEditorDocumentProcessor( , m_semanticHighlighter(document) , m_builtinProcessor(document, /*enableSemanticHighlighter=*/ false) { + connect(m_parser.data(), &ClangEditorDocumentParser::projectPartDetermined, + this, &ClangEditorDocumentProcessor::onParserDeterminedProjectPart); + // Forwarding the semantic info from the builtin processor enables us to provide all // editor (widget) related features that are not yet implemented by the clang plugin. connect(&m_builtinProcessor, &CppTools::BuiltinEditorDocumentProcessor::cppDocumentUpdated, @@ -230,9 +235,10 @@ static bool isProjectPartLoadedOrIsFallback(CppTools::ProjectPart::Ptr projectPa && (projectPart->id().isEmpty() || ClangCodeModel::Utils::isProjectPartLoaded(projectPart)); } -void ClangEditorDocumentProcessor::updateProjectPartAndTranslationUnitForEditor() +void ClangEditorDocumentProcessor::updateProjectPartAndTranslationUnitForEditor( + CppTools::ProjectPart::Ptr projectPart) { - const CppTools::ProjectPart::Ptr projectPart = m_parser->projectPart(); + QTC_ASSERT(projectPart, return); if (isProjectPartLoadedOrIsFallback(projectPart)) { updateTranslationUnitForEditor(projectPart.data()); @@ -242,6 +248,12 @@ void ClangEditorDocumentProcessor::updateProjectPartAndTranslationUnitForEditor( } } +void ClangEditorDocumentProcessor::onParserDeterminedProjectPart( + CppTools::ProjectPart::Ptr projectPart) +{ + updateProjectPartAndTranslationUnitForEditor(projectPart); +} + void ClangEditorDocumentProcessor::onParserFinished() { if (revision() != m_parserRevision) @@ -253,8 +265,6 @@ void ClangEditorDocumentProcessor::onParserFinished() // Run semantic highlighter m_semanticHighlighter.run(); - - updateProjectPartAndTranslationUnitForEditor(); } void ClangEditorDocumentProcessor::updateTranslationUnitForEditor(CppTools::ProjectPart *projectPart) @@ -294,13 +304,37 @@ void ClangEditorDocumentProcessor::requestDiagnostics() } } +static CppTools::ProjectPart projectPartForLanguageOption(CppTools::ProjectPart *projectPart) +{ + if (projectPart) + return *projectPart; + return *CppTools::CppModelManager::instance()->fallbackProjectPart().data(); +} + +static QStringList languageOptions(const QString &filePath, CppTools::ProjectPart *projectPart) +{ + const auto theProjectPart = projectPartForLanguageOption(projectPart); + CppTools::CompilerOptionsBuilder builder(theProjectPart); + builder.addLanguageOption(CppTools::ProjectFile::classify(filePath)); + + return builder.options(); +} + +static QStringList fileArguments(const QString &filePath, CppTools::ProjectPart *projectPart) +{ + return QStringList(languageOptions(filePath, projectPart)) + + CppTools::codeModelSettings()->extraClangOptions(); +} + ClangBackEnd::FileContainer ClangEditorDocumentProcessor::fileContainer(CppTools::ProjectPart *projectPart) const { - if (projectPart) - return {filePath(), projectPart->id(), revision()}; + const auto projectPartId = projectPart + ? Utf8String::fromString(projectPart->id()) + : Utf8String(); + const QStringList theFileArguments = fileArguments(filePath(), projectPart); - return {filePath(), Utf8String(), revision()}; + return {filePath(), projectPartId, Utf8StringVector(theFileArguments), revision()}; } } // namespace Internal diff --git a/src/plugins/clangcodemodel/clangeditordocumentprocessor.h b/src/plugins/clangcodemodel/clangeditordocumentprocessor.h index 4e29265aba7..575725e8738 100644 --- a/src/plugins/clangcodemodel/clangeditordocumentprocessor.h +++ b/src/plugins/clangcodemodel/clangeditordocumentprocessor.h @@ -85,10 +85,11 @@ public: static ClangEditorDocumentProcessor *get(const QString &filePath); private slots: + void onParserDeterminedProjectPart(CppTools::ProjectPart::Ptr projectPart); void onParserFinished(); private: - void updateProjectPartAndTranslationUnitForEditor(); + void updateProjectPartAndTranslationUnitForEditor(CppTools::ProjectPart::Ptr projectPart); void updateTranslationUnitForEditor(CppTools::ProjectPart *projectPart); void requestDiagnostics(CppTools::ProjectPart *projectPart); void requestDiagnostics(); diff --git a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp index 95a2e51065c..d366097624b 100644 --- a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp +++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp @@ -300,7 +300,9 @@ void ModelManagerSupportClang::onProjectPartsUpdated(ProjectExplorer::Project *p QTC_ASSERT(project, return); const CppTools::ProjectInfo projectInfo = cppModelManager()->projectInfo(project); QTC_ASSERT(projectInfo.isValid(), return); + m_ipcCommunicator.registerProjectsParts(projectInfo.projectParts()); + m_ipcCommunicator.registerFallbackProjectPart(); } void ModelManagerSupportClang::onProjectPartsRemoved(const QStringList &projectPartIds) @@ -308,6 +310,7 @@ void ModelManagerSupportClang::onProjectPartsRemoved(const QStringList &projectP if (!projectPartIds.isEmpty()) { unregisterTranslationUnitsWithProjectParts(projectPartIds); m_ipcCommunicator.unregisterProjectPartsForEditor(projectPartIds); + m_ipcCommunicator.registerFallbackProjectPart(); } } diff --git a/src/plugins/clangcodemodel/clangutils.cpp b/src/plugins/clangcodemodel/clangutils.cpp index 0c437408fdb..fd9064a3f56 100644 --- a/src/plugins/clangcodemodel/clangutils.cpp +++ b/src/plugins/clangcodemodel/clangutils.cpp @@ -104,16 +104,6 @@ static QString getResourceDir() return dir.canonicalPath(); } -static bool maybeIncludeBorlandExtensions() -{ - return -#if defined(CINDEX_VERSION) // clang 3.2 or higher - true; -#else - false; -#endif -} - class LibClangOptionsBuilder : public CompilerOptionsBuilder { public: @@ -122,13 +112,13 @@ public: if (projectPart.isNull()) return QStringList(); - LibClangOptionsBuilder optionsBuilder(projectPart); + LibClangOptionsBuilder optionsBuilder(*projectPart.data()); if (verboseRunLog().isDebugEnabled()) optionsBuilder.add(QLatin1String("-v")); optionsBuilder.addLanguageOption(fileKind); - optionsBuilder.addOptionsForLanguage(maybeIncludeBorlandExtensions()); + optionsBuilder.addOptionsForLanguage(/*checkForBorlandExtensions*/ true); optionsBuilder.addToolchainAndProjectDefines(); @@ -137,15 +127,13 @@ public: optionsBuilder.addHeaderPathOptions(); optionsBuilder.addProjectConfigFileInclude(); - optionsBuilder.addDiagnosticOptions(); - optionsBuilder.addExtraOptions(); return optionsBuilder.options(); } private: - LibClangOptionsBuilder(const CppTools::ProjectPart::Ptr &projectPart) + LibClangOptionsBuilder(const CppTools::ProjectPart &projectPart) : CompilerOptionsBuilder(projectPart) { } @@ -181,7 +169,7 @@ private: static const QString wrappedQtHeaders = ICore::instance()->resourcePath() + QLatin1String("/cplusplus/wrappedQtHeaders"); - if (m_projectPart->qtVersion != ProjectPart::NoQt) { + if (m_projectPart.qtVersion != ProjectPart::NoQt) { add(QLatin1String("-I") + wrappedQtHeaders); add(QLatin1String("-I") + wrappedQtHeaders + QLatin1String("/QtCore")); } @@ -189,9 +177,9 @@ private: void addProjectConfigFileInclude() { - if (!m_projectPart->projectConfigFile.isEmpty()) { + if (!m_projectPart.projectConfigFile.isEmpty()) { add(QLatin1String("-include")); - add(m_projectPart->projectConfigFile); + add(m_projectPart.projectConfigFile); } } @@ -201,30 +189,7 @@ private: add(QLatin1String("-fdiagnostics-show-note-include-stack")); add(QLatin1String("-fmacro-backtrace-limit=0")); add(QLatin1String("-fretain-comments-from-system-headers")); - // TODO: -Xclang -ferror-limit -Xclang 0 ? - } - - void addDiagnosticOptions() - { - add(QStringLiteral("-fmessage-length=0")); - add(QStringLiteral("-fdiagnostics-show-note-include-stack")); - add(QStringLiteral("-fmacro-backtrace-limit=0")); - add(QStringLiteral("-fretain-comments-from-system-headers")); - add(QStringLiteral("-ferror-limit=1000")); - - add(QStringLiteral("-Weverything")); - add(QStringLiteral("-Wno-c++98-compat")); - add(QStringLiteral("-Wno-c++98-compat-pedantic")); - add(QStringLiteral("-Wno-unused-macros")); - add(QStringLiteral("-Wno-newline-eof")); - add(QStringLiteral("-Wno-exit-time-destructors")); - add(QStringLiteral("-Wno-global-constructors")); - add(QStringLiteral("-Wno-gnu-zero-variadic-macro-arguments")); - add(QStringLiteral("-Wno-documentation")); - add(QStringLiteral("-Wno-shadow")); - - if (m_projectPart->languageVersion >= ProjectPart::CXX98) - add(QStringLiteral("-Wno-missing-prototypes")); + add(QLatin1String("-ferror-limit=1000")); } }; diff --git a/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp b/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp index 84b0dc4d732..58a86051931 100644 --- a/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp +++ b/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp @@ -1037,6 +1037,7 @@ void ClangCodeCompletionTest::testCompleteProjectDependingCodeInGeneratedUiFile( CppTools::Tests::ProjectOpenerAndCloser projectManager; const CppTools::ProjectInfo projectInfo = projectManager.open(projectFilePath, true); QVERIFY(projectInfo.isValid()); + QVERIFY(monitorGeneratedUiFile.waitUntilGenerated()); // Open file with ui object const QString completionFile = testDir.absolutePath("mainwindow.cpp"); @@ -1046,7 +1047,6 @@ void ClangCodeCompletionTest::testCompleteProjectDependingCodeInGeneratedUiFile( QVERIFY(openSource.succeeded()); // ...and check comletions - QVERIFY(monitorGeneratedUiFile.waitUntilGenerated()); ProposalModel proposal = completionResults(openSource.editor()); QVERIFY(hasItem(proposal, "menuBar")); QVERIFY(hasItem(proposal, "statusBar")); diff --git a/src/plugins/coreplugin/coreplugin.cpp b/src/plugins/coreplugin/coreplugin.cpp index f52fd828492..0e5c60f07e7 100644 --- a/src/plugins/coreplugin/coreplugin.cpp +++ b/src/plugins/coreplugin/coreplugin.cpp @@ -37,6 +37,7 @@ #include "modemanager.h" #include "infobar.h" #include "iwizardfactory.h" +#include "themesettings.h" #include #include @@ -48,6 +49,7 @@ #include #include +#include #include #include #include @@ -58,7 +60,6 @@ #include #include #include -#include #include using namespace Core; @@ -97,39 +98,13 @@ CorePlugin::~CorePlugin() setCreatorTheme(0); } -static QString absoluteThemePath(const QString &themeName, bool userProvidedTheme) -{ - if (themeName.isEmpty()) - return themeName; - QString res = QDir::fromNativeSeparators(themeName); - QFileInfo fi(res); - bool tryRawName = userProvidedTheme || fi.isAbsolute(); - // Try the given name - if (tryRawName && fi.exists()) - return fi.absoluteFilePath(); - const QString suffix = QLatin1String("creatortheme"); - // Try name.creatortheme - if (fi.suffix() != suffix) { - res = themeName + QLatin1Char('.') + suffix; - fi.setFile(res); - if (tryRawName && fi.exists()) - return fi.absoluteFilePath(); - } - if (fi.path().isEmpty()) - return QString(); // absolute/relative path, but not found - // If only name was given, look it up in qtcreator/themes - res.prepend(ICore::resourcePath() + QLatin1String("/themes/")); - return QFileInfo::exists(res) ? res : QString(); -} - void CorePlugin::parseArguments(const QStringList &arguments) { - const QString defaultTheme = QLatin1String("default"); - QString themeName = ICore::settings()->value( - QLatin1String(Constants::SETTINGS_THEME), defaultTheme).toString(); + const Id settingsThemeId = Id::fromSetting(ICore::settings()->value( + QLatin1String(Constants::SETTINGS_THEME), QLatin1String("default"))); + Id themeId = settingsThemeId; QColor overrideColor; bool presentationMode = false; - bool userProvidedTheme = false; for (int i = 0; i < arguments.size(); ++i) { if (arguments.at(i) == QLatin1String("-color")) { @@ -140,28 +115,27 @@ void CorePlugin::parseArguments(const QStringList &arguments) if (arguments.at(i) == QLatin1String("-presentationMode")) presentationMode = true; if (arguments.at(i) == QLatin1String("-theme")) { - themeName = arguments.at(i + 1); - userProvidedTheme = true; + themeId = Id::fromString(arguments.at(i + 1)); i++; } } - - QString themeURI = absoluteThemePath(themeName, userProvidedTheme); - if (themeURI.isEmpty()) { - themeName = defaultTheme; - themeURI = QStringLiteral("%1/themes/%2.creatortheme").arg(ICore::resourcePath()).arg(themeName); - if (themeURI.isEmpty()) { - qCritical("%s", qPrintable(QCoreApplication::translate("Application", "No valid theme \"%1\"") - .arg(themeName))); - } + const QList availableThemes = ThemeSettings::availableThemes(); + int themeIndex = Utils::indexOf(availableThemes, Utils::equal(&ThemeEntry::id, themeId)); + if (themeIndex < 0) { + themeIndex = Utils::indexOf(availableThemes, + Utils::equal(&ThemeEntry::id, settingsThemeId)); + } + if (themeIndex < 0) + themeIndex = 0; + if (themeIndex < availableThemes.size()) { + const ThemeEntry themeEntry = availableThemes.at(themeIndex); + QSettings themeSettings(themeEntry.filePath(), QSettings::IniFormat); + Theme *theme = new Theme(themeEntry.id().toString(), qApp); + theme->readSettings(themeSettings); + if (theme->flag(Theme::ApplyThemePaletteGlobally)) + QApplication::setPalette(theme->palette()); + setCreatorTheme(theme); } - - QSettings themeSettings(themeURI, QSettings::IniFormat); - Theme *theme = new Theme(themeName, qApp); - theme->readSettings(themeSettings); - if (theme->flag(Theme::ApplyThemePaletteGlobally)) - QApplication::setPalette(theme->palette()); - setCreatorTheme(theme); // defer creation of these widgets until here, // because they need a valid theme set @@ -176,6 +150,10 @@ void CorePlugin::parseArguments(const QStringList &arguments) bool CorePlugin::initialize(const QStringList &arguments, QString *errorMessage) { + if (ThemeSettings::availableThemes().isEmpty()) { + *errorMessage = tr("No themes found in installation."); + return false; + } new ActionManager(this); Theme::initialPalette(); // Initialize palette before setting it qsrand(QDateTime::currentDateTime().toTime_t()); diff --git a/src/plugins/coreplugin/dialogs/newdialog.cpp b/src/plugins/coreplugin/dialogs/newdialog.cpp index aae3940ed88..f60e9388dc2 100644 --- a/src/plugins/coreplugin/dialogs/newdialog.cpp +++ b/src/plugins/coreplugin/dialogs/newdialog.cpp @@ -189,6 +189,7 @@ using namespace Core::Internal; bool NewDialog::m_isRunning = false; QString NewDialog::m_lastCategory = QString(); +QString NewDialog::m_lastPlatform = QString(); NewDialog::NewDialog(QWidget *parent) : QDialog(parent), @@ -301,6 +302,9 @@ void NewDialog::showDialog() { QModelIndex idx; + if (!m_lastPlatform.isEmpty()) + m_ui->comboBox->setCurrentIndex(m_ui->comboBox->findData(m_lastPlatform)); + if (!m_lastCategory.isEmpty()) foreach (QStandardItem* item, m_categoryItems) { if (item->data(Qt::UserRole) == m_lastCategory) @@ -446,6 +450,7 @@ void NewDialog::saveState() QStandardItem *currentItem = m_model->itemFromIndex(m_twoLevelProxyModel->mapToSource(idx)); if (currentItem) m_lastCategory = currentItem->data(Qt::UserRole).toString(); + m_lastPlatform = m_ui->comboBox->currentData().toString(); } void NewDialog::accept() diff --git a/src/plugins/coreplugin/dialogs/newdialog.h b/src/plugins/coreplugin/dialogs/newdialog.h index c18efef58db..03ec94b1272 100644 --- a/src/plugins/coreplugin/dialogs/newdialog.h +++ b/src/plugins/coreplugin/dialogs/newdialog.h @@ -86,6 +86,7 @@ private: void saveState(); static QString m_lastCategory; + static QString m_lastPlatform; static bool m_isRunning; Ui::NewDialog *m_ui; diff --git a/src/plugins/coreplugin/dialogs/shortcutsettings.cpp b/src/plugins/coreplugin/dialogs/shortcutsettings.cpp index 94f78cf5d50..c91016d3f97 100644 --- a/src/plugins/coreplugin/dialogs/shortcutsettings.cpp +++ b/src/plugins/coreplugin/dialogs/shortcutsettings.cpp @@ -373,12 +373,15 @@ bool ShortcutSettingsWidget::filterColumn(const QString &filterString, QTreeWidg int column) const { QString text; - if (column == item->columnCount() - 1) { + if (column == item->columnCount() - 1) { // shortcut // filter on the shortcut edit text if (!item->data(0, Qt::UserRole).isValid()) return true; ShortcutItem *scitem = qvariant_cast(item->data(0, Qt::UserRole)); text = keySequenceToEditString(scitem->m_key); + } else if (column == 0 && item->data(0, Qt::UserRole).isValid()) { // command id + ShortcutItem *scitem = qvariant_cast(item->data(0, Qt::UserRole)); + text = scitem->m_cmd->id().toString(); } else { text = item->text(column); } diff --git a/src/plugins/coreplugin/editormanager/editormanager.cpp b/src/plugins/coreplugin/editormanager/editormanager.cpp index b8a2bfbde04..c84cb567ef7 100644 --- a/src/plugins/coreplugin/editormanager/editormanager.cpp +++ b/src/plugins/coreplugin/editormanager/editormanager.cpp @@ -490,10 +490,6 @@ void EditorManagerPrivate::init() connect(m_autoSaveTimer, SIGNAL(timeout()), SLOT(autoSave())); updateAutoSave(); - // Do not ask for files to save. - // MainWindow::closeEvent has already done that. - ICore::addPreCloseListener([]() -> bool { return EditorManager::closeAllEditors(false); }); - d->m_openEditorsFactory = new OpenEditorsViewFactory(); ExtensionSystem::PluginManager::addObject(d->m_openEditorsFactory); @@ -515,7 +511,14 @@ void EditorManagerPrivate::init() []() -> int { IEditor *editor = EditorManager::currentEditor(); return editor ? editor->widget()->mapToGlobal(QPoint(0, 0)).y() : 0; - }); + }); +} + +void EditorManagerPrivate::extensionsInitialized() +{ + // Do not ask for files to save. + // MainWindow::closeEvent has already done that. + ICore::addPreCloseListener([]() -> bool { return EditorManager::closeAllEditors(false); }); } EditorManagerPrivate *EditorManagerPrivate::instance() @@ -2487,7 +2490,9 @@ static void mimeTypeFactoryLookup(const Utils::MimeType &mimeType, // * application/octet-stream // * text/plain QList queue; + QSet seen; queue.append(mimeType); + seen.insert(mimeType.name()); while (!queue.isEmpty()) { Utils::MimeType mt = queue.takeFirst(); // check for matching factories @@ -2507,8 +2512,14 @@ static void mimeTypeFactoryLookup(const Utils::MimeType &mimeType, QStringList parentNames = mt.parentMimeTypes(); foreach (const QString &parentName, parentNames) { const Utils::MimeType parent = mdb.mimeTypeForName(parentName); - if (parent.isValid()) - queue.append(parent); + if (parent.isValid()) { + int seenSize = seen.size(); + seen.insert(parent.name()); + if (seen.size() != seenSize) // not seen before, so add + queue.append(parent); + else + qWarning("MimeTypes: Parent hierarchy loop detected for '%s'!", qPrintable(parent.name())); + } } } } diff --git a/src/plugins/coreplugin/editormanager/editormanager_p.h b/src/plugins/coreplugin/editormanager/editormanager_p.h index 21047095122..d12c59851ab 100644 --- a/src/plugins/coreplugin/editormanager/editormanager_p.h +++ b/src/plugins/coreplugin/editormanager/editormanager_p.h @@ -69,6 +69,8 @@ class EditorManagerPrivate : public QObject public: static EditorManagerPrivate *instance(); + static void extensionsInitialized(); // only use from MainWindow + static EditorArea *mainEditorArea(); static EditorView *currentEditorView(); static void setCurrentEditor(IEditor *editor, bool ignoreNavigationHistory = false); diff --git a/src/plugins/coreplugin/fancyactionbar.cpp b/src/plugins/coreplugin/fancyactionbar.cpp index d4ecf132cd2..5d89a184a28 100644 --- a/src/plugins/coreplugin/fancyactionbar.cpp +++ b/src/plugins/coreplugin/fancyactionbar.cpp @@ -53,9 +53,13 @@ using namespace Utils; namespace Core { namespace Internal { -FancyToolButton::FancyToolButton(QWidget *parent) +FancyToolButton::FancyToolButton(QAction *action, QWidget *parent) : QToolButton(parent), m_fader(0) { + setDefaultAction(action); + connect(action, &QAction::changed, this, &FancyToolButton::actionChanged); + actionChanged(); + setAttribute(Qt::WA_Hover, true); setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); } @@ -364,18 +368,12 @@ FancyActionBar::FancyActionBar(QWidget *parent) void FancyActionBar::addProjectSelector(QAction *action) { - FancyToolButton* toolButton = new FancyToolButton(this); - toolButton->setDefaultAction(action); - connect(action, SIGNAL(changed()), toolButton, SLOT(actionChanged())); - m_actionsLayout->insertWidget(0, toolButton); + m_actionsLayout->insertWidget(0, new FancyToolButton(action, this)); } void FancyActionBar::insertAction(int index, QAction *action) { - FancyToolButton *toolButton = new FancyToolButton(this); - toolButton->setDefaultAction(action); - connect(action, SIGNAL(changed()), toolButton, SLOT(actionChanged())); - m_actionsLayout->insertWidget(index, toolButton); + m_actionsLayout->insertWidget(index, new FancyToolButton(action, this)); } QLayout *FancyActionBar::actionsLayout() const diff --git a/src/plugins/coreplugin/fancyactionbar.h b/src/plugins/coreplugin/fancyactionbar.h index 90a96062972..3d5c3568ba4 100644 --- a/src/plugins/coreplugin/fancyactionbar.h +++ b/src/plugins/coreplugin/fancyactionbar.h @@ -47,7 +47,7 @@ class FancyToolButton : public QToolButton Q_PROPERTY(float fader READ fader WRITE setFader) public: - FancyToolButton(QWidget *parent = 0); + FancyToolButton(QAction *action, QWidget *parent = 0); void paintEvent(QPaintEvent *event); bool event(QEvent *e); diff --git a/src/plugins/coreplugin/find/ifindfilter.cpp b/src/plugins/coreplugin/find/ifindfilter.cpp index 68e774fc9ec..2fbdb8a8f5a 100644 --- a/src/plugins/coreplugin/find/ifindfilter.cpp +++ b/src/plugins/coreplugin/find/ifindfilter.cpp @@ -44,7 +44,7 @@ a text based search term (potentially with find flags like searching case sensitively or using regular expressions). Existing scopes are \gui {All Projects} that searches from all files in all projects - and \gui {Files on File System} where the user provides a directory and file + and \gui {Files in File System} where the user provides a directory and file patterns to search. To make your find scope available to the user, you need to implement this diff --git a/src/plugins/coreplugin/locator/locatorwidget.cpp b/src/plugins/coreplugin/locator/locatorwidget.cpp index 21f0b50026f..61dd506ace7 100644 --- a/src/plugins/coreplugin/locator/locatorwidget.cpp +++ b/src/plugins/coreplugin/locator/locatorwidget.cpp @@ -410,12 +410,12 @@ bool LocatorWidget::eventFilter(QObject *obj, QEvent *event) } } else if (obj == m_fileLineEdit && event->type() == QEvent::FocusOut) { QFocusEvent *fev = static_cast(event); - if (fev->reason() != Qt::ActiveWindowFocusReason || !m_completionList->isActiveWindow()) { + if (fev->reason() != Qt::ActiveWindowFocusReason || !m_completionList->isActiveWindow()) m_completionList->hide(); - m_fileLineEdit->clearFocus(); - } } else if (obj == m_fileLineEdit && event->type() == QEvent::FocusIn) { - showPopupNow(); + QFocusEvent *fev = static_cast(event); + if (fev->reason() != Qt::ActiveWindowFocusReason) + showPopupNow(); } else if (obj == this && event->type() == QEvent::ShortcutOverride) { QKeyEvent *ke = static_cast(event); switch (ke->key()) { @@ -584,10 +584,8 @@ void LocatorWidget::show(const QString &text, int selectionStart, int selectionL { if (!text.isEmpty()) m_fileLineEdit->setText(text); - if (!m_fileLineEdit->hasFocus()) - m_fileLineEdit->setFocus(); - else - showPopupNow(); + m_fileLineEdit->setFocus(); + showPopupNow(); ICore::raiseWindow(ICore::mainWindow()); if (selectionStart >= 0) { diff --git a/src/plugins/coreplugin/locator/spotlightlocatorfilter.mm b/src/plugins/coreplugin/locator/spotlightlocatorfilter.mm index cb29e2c3c70..3744ce79ca9 100644 --- a/src/plugins/coreplugin/locator/spotlightlocatorfilter.mm +++ b/src/plugins/coreplugin/locator/spotlightlocatorfilter.mm @@ -30,7 +30,6 @@ #include "spotlightlocatorfilter.h" -#include #include #include @@ -38,7 +37,6 @@ #include #include -#include #include #include #include @@ -85,33 +83,34 @@ SpotlightIterator::SpotlightIterator(const QString &expression) m_queueIndex(-1), m_finished(false) { - Utils::AutoreleasePool pool; Q_UNUSED(pool) - NSPredicate *predicate = [NSPredicate predicateWithFormat:expression.toNSString()]; - m_query = [[NSMetadataQuery alloc] init]; - m_query.predicate = predicate; - m_query.searchScopes = [NSArray arrayWithObject:NSMetadataQueryLocalComputerScope]; - m_queue = [[NSMutableArray alloc] init]; - m_progressObserver = [[[NSNotificationCenter defaultCenter] - addObserverForName:NSMetadataQueryGatheringProgressNotification - object:m_query - queue:nil - usingBlock:^(NSNotification *note) { - [m_query disableUpdates]; - QMutexLocker lock(&m_mutex); Q_UNUSED(lock) - [m_queue addObjectsFromArray:[note.userInfo objectForKey:(NSString *)kMDQueryUpdateAddedItems]]; - [m_query enableUpdates]; - m_waitForItems.wakeAll(); - }] retain]; - m_finishObserver = [[[NSNotificationCenter defaultCenter] - addObserverForName:NSMetadataQueryDidFinishGatheringNotification - object:m_query - queue:nil - usingBlock:^(NSNotification *) { - QMutexLocker lock(&m_mutex); Q_UNUSED(lock) - m_finished = true; - m_waitForItems.wakeAll(); - }] retain]; - [m_query startQuery]; + @autoreleasepool { + NSPredicate *predicate = [NSPredicate predicateWithFormat:expression.toNSString()]; + m_query = [[NSMetadataQuery alloc] init]; + m_query.predicate = predicate; + m_query.searchScopes = [NSArray arrayWithObject:NSMetadataQueryLocalComputerScope]; + m_queue = [[NSMutableArray alloc] init]; + m_progressObserver = [[[NSNotificationCenter defaultCenter] + addObserverForName:NSMetadataQueryGatheringProgressNotification + object:m_query + queue:nil + usingBlock:^(NSNotification *note) { + [m_query disableUpdates]; + QMutexLocker lock(&m_mutex); Q_UNUSED(lock) + [m_queue addObjectsFromArray:[note.userInfo objectForKey:(NSString *)kMDQueryUpdateAddedItems]]; + [m_query enableUpdates]; + m_waitForItems.wakeAll(); + }] retain]; + m_finishObserver = [[[NSNotificationCenter defaultCenter] + addObserverForName:NSMetadataQueryDidFinishGatheringNotification + object:m_query + queue:nil + usingBlock:^(NSNotification *) { + QMutexLocker lock(&m_mutex); Q_UNUSED(lock) + m_finished = true; + m_waitForItems.wakeAll(); + }] retain]; + [m_query startQuery]; + } } SpotlightIterator::~SpotlightIterator() @@ -163,22 +162,23 @@ void SpotlightIterator::ensureNext() return; if (m_index >= 10000) // limit the amount of data that is passed on return; - Utils::AutoreleasePool pool; Q_UNUSED(pool) - // check if there are items in the queue, otherwise wait for some - m_mutex.lock(); - bool itemAvailable = (m_queueIndex + 1 < m_queue.count); - if (!itemAvailable && !m_finished) { - m_waitForItems.wait(&m_mutex); - itemAvailable = (m_queueIndex + 1 < m_queue.count); - } - if (itemAvailable) { - ++m_queueIndex; - NSMetadataItem *item = [m_queue objectAtIndex:m_queueIndex]; - m_filePaths.append(QString::fromNSString([item valueForAttribute:NSMetadataItemPathKey])); - m_fileNames.append(QString::fromNSString([item valueForAttribute:NSMetadataItemFSNameKey])); + @autoreleasepool { + // check if there are items in the queue, otherwise wait for some + m_mutex.lock(); + bool itemAvailable = (m_queueIndex + 1 < m_queue.count); + if (!itemAvailable && !m_finished) { + m_waitForItems.wait(&m_mutex); + itemAvailable = (m_queueIndex + 1 < m_queue.count); + } + if (itemAvailable) { + ++m_queueIndex; + NSMetadataItem *item = [m_queue objectAtIndex:m_queueIndex]; + m_filePaths.append(QString::fromNSString([item valueForAttribute:NSMetadataItemPathKey])); + m_fileNames.append(QString::fromNSString([item valueForAttribute:NSMetadataItemFSNameKey])); + } + m_mutex.unlock(); } - m_mutex.unlock(); } // #pragma mark -- SpotlightLocatorFilter diff --git a/src/plugins/coreplugin/mainwindow.cpp b/src/plugins/coreplugin/mainwindow.cpp index f1adda1e431..5029d78a6c4 100644 --- a/src/plugins/coreplugin/mainwindow.cpp +++ b/src/plugins/coreplugin/mainwindow.cpp @@ -347,6 +347,7 @@ bool MainWindow::init(QString *errorMessage) void MainWindow::extensionsInitialized() { + EditorManagerPrivate::extensionsInitialized(); MimeTypeSettings::restoreSettings(); m_windowSupport = new WindowSupport(this, Context("Core.MainWindow")); m_windowSupport->setCloseActionEnabled(false); diff --git a/src/plugins/coreplugin/themeeditor/themesettingstablemodel.cpp b/src/plugins/coreplugin/themeeditor/themesettingstablemodel.cpp index 13a2accda8a..c693fb304aa 100644 --- a/src/plugins/coreplugin/themeeditor/themesettingstablemodel.cpp +++ b/src/plugins/coreplugin/themeeditor/themesettingstablemodel.cpp @@ -222,7 +222,7 @@ void ThemeSettingsTableModel::initFrom(Theme *theme) } m_widgetStyle = theme->widgetStyle(); - m_name = theme->d->name; + m_displayName = theme->d->displayName; m_preferredStyles = theme->d->preferredStyles; } @@ -252,7 +252,7 @@ void ThemeSettingsTableModel::toTheme(Theme *t) const } theme->widgetStyle = m_widgetStyle; - theme->name = m_name; + theme->displayName = m_displayName; theme->preferredStyles = m_preferredStyles; } diff --git a/src/plugins/coreplugin/themeeditor/themesettingstablemodel.h b/src/plugins/coreplugin/themeeditor/themesettingstablemodel.h index 63a79947293..013d5a6d517 100644 --- a/src/plugins/coreplugin/themeeditor/themesettingstablemodel.h +++ b/src/plugins/coreplugin/themeeditor/themesettingstablemodel.h @@ -76,7 +76,7 @@ public: void initFrom(Utils::Theme *theme); void toTheme(Utils::Theme *theme) const; - QString m_name; + QString m_displayName; QStringList m_preferredStyles; public: diff --git a/src/plugins/coreplugin/themesettings.cpp b/src/plugins/coreplugin/themesettings.cpp index 4bad6936729..8f9f00a5285 100644 --- a/src/plugins/coreplugin/themesettings.cpp +++ b/src/plugins/coreplugin/themesettings.cpp @@ -31,14 +31,55 @@ #include "themesettings.h" #include "themesettingswidget.h" #include "coreconstants.h" +#include "icore.h" + +#include #include +#include +#include +#include + +static const char themeNameKey[] = "ThemeName"; namespace Core { namespace Internal { -ThemeSettings::ThemeSettings() : - m_widget(0) +ThemeEntry::ThemeEntry(Id id, const QString &filePath, bool readOnly) + : m_id(id), + m_filePath(filePath), + m_readOnly(readOnly) +{ +} + +Id ThemeEntry::id() const +{ + return m_id; +} + +QString ThemeEntry::displayName() const +{ + if (m_displayName.isEmpty() && !m_filePath.isEmpty()) { + QSettings settings(m_filePath, QSettings::IniFormat); + m_displayName = settings.value(QLatin1String(themeNameKey), + QCoreApplication::tr("unnamed")).toString(); + if (false) // TODO: Revert to m_readOnly + m_displayName = QCoreApplication::tr("%1 (built-in)").arg(m_displayName); + } + return m_displayName; +} + +QString ThemeEntry::filePath() const +{ + return m_filePath; +} + +bool ThemeEntry::readOnly() const +{ + return m_readOnly; +} + +ThemeSettings::ThemeSettings() { setId(Constants::SETTINGS_ID_INTERFACE); setDisplayName(tr("Theme")); @@ -71,5 +112,38 @@ void ThemeSettings::finish() m_widget = 0; } +static void addThemesFromPath(const QString &path, bool readOnly, QList *themes) +{ + static const QLatin1String extension(".creatortheme"); + QDir themeDir(path); + themeDir.setNameFilters(QStringList() << QLatin1String("*.creatortheme")); + themeDir.setFilter(QDir::Files); + const QStringList themeList = themeDir.entryList(); + foreach (const QString &fileName, themeList) { + QString id = QFileInfo(fileName).completeBaseName(); + themes->append(ThemeEntry(Id::fromString(id), themeDir.absoluteFilePath(fileName), readOnly)); + } +} + +QList ThemeSettings::availableThemes() +{ + QList themes; + + static const QString installThemeDir = ICore::resourcePath() + QLatin1String("/themes"); + static const QString userThemeDir = ICore::userResourcePath() + QLatin1String("/themes"); + addThemesFromPath(installThemeDir, /*readOnly=*/true, &themes); + if (themes.isEmpty()) + qWarning() << "Warning: No themes found in installation: " + << QDir::toNativeSeparators(installThemeDir); + // move default theme to front + int defaultIndex = Utils::indexOf(themes, Utils::equal(&ThemeEntry::id, Id("default"))); + if (defaultIndex > 0) { // == exists and not at front + ThemeEntry defaultEntry = themes.takeAt(defaultIndex); + themes.prepend(defaultEntry); + } + addThemesFromPath(userThemeDir, /*readOnly=*/false, &themes); + return themes; +} + } // namespace Internal } // namespace Core diff --git a/src/plugins/coreplugin/themesettings.h b/src/plugins/coreplugin/themesettings.h index 415617b69bc..617dd74e676 100644 --- a/src/plugins/coreplugin/themesettings.h +++ b/src/plugins/coreplugin/themesettings.h @@ -31,6 +31,8 @@ #ifndef THEMESETTINGS_H #define THEMESETTINGS_H +#include "id.h" + #include namespace Core { @@ -38,6 +40,24 @@ namespace Internal { class ThemeSettingsWidget; +class ThemeEntry +{ +public: + ThemeEntry() = default; + ThemeEntry(Id id, const QString &filePath, bool readOnly); + + Id id() const; + QString displayName() const; + QString filePath() const; + bool readOnly() const; + +private: + Id m_id; + QString m_filePath; + mutable QString m_displayName; + bool m_readOnly = true; +}; + class ThemeSettings : public IOptionsPage { Q_OBJECT @@ -50,7 +70,9 @@ public: void apply(); void finish(); - ThemeSettingsWidget *m_widget; + static QList availableThemes(); +private: + ThemeSettingsWidget *m_widget = 0; }; } // namespace Internal diff --git a/src/plugins/coreplugin/themesettingswidget.cpp b/src/plugins/coreplugin/themesettingswidget.cpp index f098762dc24..7bd14a2c113 100644 --- a/src/plugins/coreplugin/themesettingswidget.cpp +++ b/src/plugins/coreplugin/themesettingswidget.cpp @@ -33,7 +33,9 @@ #include "icore.h" #include "manhattanstyle.h" #include "themeeditor/themesettingstablemodel.h" +#include "themesettings.h" +#include #include #include #include @@ -52,8 +54,6 @@ using namespace Utils; namespace Core { namespace Internal { -const char themeNameKey[] = "ThemeName"; - static QString customThemesPath() { return ICore::userResourcePath() + QLatin1String("/themes/"); @@ -81,42 +81,6 @@ static QString createThemeFileName(const QString &pattern) return fileName; } - -struct ThemeEntry -{ - ThemeEntry() : m_readOnly(true) {} - ThemeEntry(const QString &name, const QString &filePath, bool readOnly): - m_name(name), - m_filePath(filePath), - m_readOnly(readOnly) - { - } - - QString name() const { return m_name; } - QString displayName() const; - QString filePath() const { return m_filePath; } - bool readOnly() const { return m_readOnly; } - -private: - QString m_name; - QString m_filePath; - mutable QString m_displayName; - bool m_readOnly; -}; - -QString ThemeEntry::displayName() const -{ - if (m_displayName.isEmpty()) { - QSettings settings(filePath(), QSettings::IniFormat); - m_displayName = settings.value(QLatin1String(themeNameKey), - QCoreApplication::tr("unnamed")).toString(); - if (false) // TODO: Revert to m_readOnly - m_displayName = QCoreApplication::tr("%1 (built-in)").arg(m_displayName); - } - return m_displayName; -} - - class ThemeListModel : public QAbstractListModel { public: @@ -179,7 +143,7 @@ ThemeSettingsPrivate::ThemeSettingsPrivate(QWidget *widget) , m_refreshingThemeList(false) , m_ui(new Ui::ThemeSettings) { - m_currentTheme = ThemeEntry(creatorTheme()->name(), creatorTheme()->filePath(), true); + m_currentTheme = ThemeEntry(Id::fromString(creatorTheme()->id()), creatorTheme()->filePath(), true); m_ui->setupUi(widget); // TODO: Restore the editor and the buttons after improving the editor m_ui->editor->hide(); @@ -216,44 +180,15 @@ ThemeSettingsWidget::~ThemeSettingsWidget() void ThemeSettingsWidget::refreshThemeList() { - QList themes; - - QDir themeDir(ICore::resourcePath() + QLatin1String("/themes")); - themeDir.setNameFilters(QStringList() << QLatin1String("*.creatortheme")); - themeDir.setFilter(QDir::Files); - - int selected = 0; - - QStringList themeList = themeDir.entryList(); - const QString defaultTheme = QLatin1String("default.creatortheme"); - if (themeList.removeOne(defaultTheme)) - themeList.prepend(defaultTheme); - const QLatin1String extension(".creatortheme"); - for (int i = 0, total = themeList.count(); i < total; ++i) { - const QString fileName = themeList.at(i); - if (d->m_currentTheme.name() + extension == fileName) - selected = i; - QString name = fileName; - name.remove(extension); - themes.append(ThemeEntry(name, themeDir.absoluteFilePath(fileName), true)); - } - - if (themes.isEmpty()) - qWarning() << "Warning: no themes found in path:" << themeDir.path(); - - themeDir.setPath(customThemesPath()); - foreach (const QString &file, themeDir.entryList()) { - const QString fileName = themeDir.absoluteFilePath(file); - if (d->m_currentTheme.name() == fileName) - selected = themes.size(); - themes.append(ThemeEntry(fileName, fileName, false)); - } - - d->m_currentTheme = themes[selected]; + const QList themes = ThemeSettings::availableThemes(); + const int selected = Utils::indexOf(themes, Utils::equal(&ThemeEntry::id, d->m_currentTheme.id())); d->m_refreshingThemeList = true; d->m_themeListModel->setThemes(themes); - d->m_ui->themeComboBox->setCurrentIndex(selected); + if (selected >= 0) { + d->m_currentTheme = themes[selected]; + d->m_ui->themeComboBox->setCurrentIndex(selected); + } d->m_refreshingThemeList = false; } @@ -270,7 +205,7 @@ void ThemeSettingsWidget::themeSelected(int index) d->m_currentTheme = entry; QSettings settings(entry.filePath(), QSettings::IniFormat); - Theme theme(entry.name()); + Theme theme(entry.id().toString()); theme.readSettings(settings); d->m_ui->editor->initFrom(&theme); } @@ -354,7 +289,7 @@ void ThemeSettingsWidget::maybeSaveTheme() messageBox->setDefaultButton(QMessageBox::Save); if (messageBox->exec() == QMessageBox::Save) { - Theme newTheme(d->m_currentTheme.name()); + Theme newTheme(d->m_currentTheme.id().toString()); d->m_ui->editor->model()->toTheme(&newTheme); newTheme.writeSettings(d->m_currentTheme.filePath()); } @@ -373,7 +308,7 @@ void ThemeSettingsWidget::renameTheme() dialog->setInputMode(QInputDialog::TextInput); dialog->setWindowTitle(tr("Rename Theme")); dialog->setLabelText(tr("Theme name:")); - dialog->setTextValue(d->m_ui->editor->model()->m_name); + dialog->setTextValue(d->m_ui->editor->model()->m_displayName); int ret = dialog->exec(); QString newName = dialog->textValue(); delete dialog; @@ -382,9 +317,9 @@ void ThemeSettingsWidget::renameTheme() return; // overwrite file with new name - Theme newTheme(entry.name()); + Theme newTheme(entry.id().toString()); d->m_ui->editor->model()->toTheme(&newTheme); - newTheme.setName(newName); + newTheme.setDisplayName(newName); newTheme.writeSettings(entry.filePath()); refreshThemeList(); @@ -401,8 +336,9 @@ void ThemeSettingsWidget::copyThemeByName(const QString &name) QString baseFileName = QFileInfo(entry.filePath()).completeBaseName(); baseFileName += QLatin1String("_copy%1.creatortheme"); QString fileName = createThemeFileName(baseFileName); + QString id = QFileInfo(fileName).completeBaseName(); - if (fileName.isEmpty()) + if (fileName.isEmpty() || id.isEmpty()) return; // Ask about saving any existing modifactions @@ -410,18 +346,18 @@ void ThemeSettingsWidget::copyThemeByName(const QString &name) Theme newTheme(fileName); d->m_ui->editor->model()->toTheme(&newTheme); - newTheme.setName(name); + newTheme.setDisplayName(name); newTheme.writeSettings(fileName); - d->m_currentTheme = ThemeEntry(fileName, fileName, true); + d->m_currentTheme = ThemeEntry(Id::fromString(id), fileName, true); refreshThemeList(); } void ThemeSettingsWidget::apply() { - const QString themeName = d->m_currentTheme.name(); - Theme *newTheme = new Theme(themeName); + const QString themeId = d->m_currentTheme.id().toString(); + Theme *newTheme = new Theme(themeId); if (d->m_currentTheme.readOnly()) { QSettings themeSettings(d->m_currentTheme.filePath(), QSettings::IniFormat); newTheme->readSettings(themeSettings); @@ -447,7 +383,7 @@ void ThemeSettingsWidget::apply() // save filename of selected theme in global config QSettings *settings = ICore::settings(); - settings->setValue(QLatin1String(Constants::SETTINGS_THEME), themeName); + settings->setValue(QLatin1String(Constants::SETTINGS_THEME), themeId); } } // namespace Internal diff --git a/src/plugins/cpptools/cppcodemodelsettings.cpp b/src/plugins/cpptools/cppcodemodelsettings.cpp index 35555868a13..5d35cf7d3e8 100644 --- a/src/plugins/cpptools/cppcodemodelsettings.cpp +++ b/src/plugins/cpptools/cppcodemodelsettings.cpp @@ -36,6 +36,7 @@ using namespace CppTools; static QLatin1String cppHeaderMimeType(Constants::CPP_HEADER_MIMETYPE); static QLatin1String cHeaderMimeType(Constants::C_HEADER_MIMETYPE); +static QLatin1String clangExtraOptionsKey(Constants::CPPTOOLS_EXTRA_CLANG_OPTIONS); void CppCodeModelSettings::fromSettings(QSettings *s) { @@ -45,6 +46,8 @@ void CppCodeModelSettings::fromSettings(QSettings *s) foreach (const QString &mimeType, supportedMimeTypes()) setIdForMimeType(supporters, mimeType); + setExtraClangOptions(s->value(clangExtraOptionsKey, defaultExtraClangOptions()).toStringList()); + QVariant v = s->value(QLatin1String(Constants::CPPTOOLS_MODEL_MANAGER_PCH_USAGE), PchUse_None); setPCHUsage(static_cast(v.toInt())); s->endGroup(); @@ -59,6 +62,7 @@ void CppCodeModelSettings::toSettings(QSettings *s) foreach (const QString &mimeType, m_modelManagerSupportByMimeType.keys()) var[mimeType] = m_modelManagerSupportByMimeType[mimeType]; s->setValue(QLatin1String(Constants::CPPTOOLS_MODEL_MANAGER_SUPPORTERS_KEY), QVariant(var)); + s->setValue(clangExtraOptionsKey, extraClangOptions()); s->setValue(QLatin1String(Constants::CPPTOOLS_MODEL_MANAGER_PCH_USAGE), pchUsage()); s->endGroup(); @@ -116,6 +120,33 @@ bool CppCodeModelSettings::hasModelManagerSupportIdForMimeType(const QString &mi return m_modelManagerSupportByMimeType.value(mimeType) == id; } +QStringList CppCodeModelSettings::defaultExtraClangOptions() +{ + return { + QStringLiteral("-Weverything"), + QStringLiteral("-Wno-c++98-compat"), + QStringLiteral("-Wno-c++98-compat-pedantic"), + QStringLiteral("-Wno-unused-macros"), + QStringLiteral("-Wno-newline-eof"), + QStringLiteral("-Wno-exit-time-destructors"), + QStringLiteral("-Wno-global-constructors"), + QStringLiteral("-Wno-gnu-zero-variadic-macro-arguments"), + QStringLiteral("-Wno-documentation"), + QStringLiteral("-Wno-shadow"), + QStringLiteral("-Wno-missing-prototypes"), // Not optimal for C projects. + }; +} + +QStringList CppCodeModelSettings::extraClangOptions() const +{ + return m_extraClangOptions; +} + +void CppCodeModelSettings::setExtraClangOptions(const QStringList &extraClangOptions) +{ + m_extraClangOptions = extraClangOptions; +} + void CppCodeModelSettings::setIdForMimeType(const QVariant &var, const QString &mimeType) { QHash mimeToId = var.toHash(); diff --git a/src/plugins/cpptools/cppcodemodelsettings.h b/src/plugins/cpptools/cppcodemodelsettings.h index 8c07c1671f1..2d14d24bd65 100644 --- a/src/plugins/cpptools/cppcodemodelsettings.h +++ b/src/plugins/cpptools/cppcodemodelsettings.h @@ -73,6 +73,10 @@ public: void setDefaultId(const QString &defaultId) { m_defaultId = defaultId; } + static QStringList defaultExtraClangOptions(); + QStringList extraClangOptions() const; + void setExtraClangOptions(const QStringList &extraClangOptions); + PCHUsage pchUsage() const { return m_pchUsage; } void setPCHUsage(PCHUsage pchUsage) { m_pchUsage = pchUsage; } @@ -90,6 +94,7 @@ private: private: QHash m_modelManagerSupportByMimeType; QHash m_modelManagerSupportsByName; + QStringList m_extraClangOptions; QString m_defaultId; PCHUsage m_pchUsage; }; diff --git a/src/plugins/cpptools/cppcodemodelsettingspage.cpp b/src/plugins/cpptools/cppcodemodelsettingspage.cpp index c09c5080bdc..f3295e776b7 100644 --- a/src/plugins/cpptools/cppcodemodelsettingspage.cpp +++ b/src/plugins/cpptools/cppcodemodelsettingspage.cpp @@ -46,7 +46,11 @@ CppCodeModelSettingsWidget::CppCodeModelSettingsWidget(QWidget *parent) { m_ui->setupUi(this); - m_ui->theGroupBox->setVisible(true); + m_ui->clangSettingsGroupBox->setVisible(true); + connect(m_ui->clangOptionsResetButton, &QPushButton::clicked, [this]() { + const QString options = m_settings->defaultExtraClangOptions().join(QLatin1Char('\n')); + m_ui->clangOptionsToAppendTextEdit->document()->setPlainText(options); + }); } CppCodeModelSettingsWidget::~CppCodeModelSettingsWidget() @@ -58,41 +62,16 @@ void CppCodeModelSettingsWidget::setSettings(const QSharedPointercChooser, QLatin1String(Constants::C_SOURCE_MIMETYPE)); - applyToWidget(m_ui->cppChooser, QLatin1String(Constants::CPP_SOURCE_MIMETYPE)); - applyToWidget(m_ui->objcChooser, QLatin1String(Constants::OBJECTIVE_C_SOURCE_MIMETYPE)); - applyToWidget(m_ui->objcppChooser, QLatin1String(Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE)); - applyToWidget(m_ui->hChooser, QLatin1String(Constants::C_HEADER_MIMETYPE)); - + setupClangCodeModelWidgets(); m_ui->ignorePCHCheckBox->setChecked(s->pchUsage() == CppCodeModelSettings::PchUse_None); } -void CppCodeModelSettingsWidget::applyToWidget(QComboBox *chooser, const QString &mimeType) const -{ - chooser->clear(); - - QStringList names = m_settings->availableModelManagerSupportProvidersByName().keys(); - Utils::sort(names); - foreach (const QString &name, names) { - const QString &id = m_settings->availableModelManagerSupportProvidersByName()[name]; - chooser->addItem(name, id); - if (id == m_settings->modelManagerSupportIdForMimeType(mimeType)) - chooser->setCurrentIndex(chooser->count() - 1); - } - chooser->setEnabled(names.size() > 1); -} - void CppCodeModelSettingsWidget::applyToSettings() const { bool changed = false; - changed |= applyToSettings(m_ui->cChooser, QLatin1String(Constants::C_SOURCE_MIMETYPE)); - changed |= applyToSettings(m_ui->cppChooser, QLatin1String(Constants::CPP_SOURCE_MIMETYPE)); - changed |= applyToSettings(m_ui->objcChooser, - QLatin1String(Constants::OBJECTIVE_C_SOURCE_MIMETYPE)); - changed |= applyToSettings(m_ui->objcppChooser, - QLatin1String(Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE)); - changed |= applyToSettings(m_ui->hChooser, - QLatin1String(Constants::C_HEADER_MIMETYPE)); + + if (applyClangCodeModelWidgetsToSettings()) + changed = true; if (m_ui->ignorePCHCheckBox->isChecked() != (m_settings->pchUsage() == CppCodeModelSettings::PchUse_None)) { @@ -106,14 +85,46 @@ void CppCodeModelSettingsWidget::applyToSettings() const m_settings->toSettings(Core::ICore::settings()); } -bool CppCodeModelSettingsWidget::applyToSettings(QComboBox *chooser, const QString &mimeType) const +static bool isClangCodeModelActive(const CppCodeModelSettings &settings) { - QString newId = chooser->itemData(chooser->currentIndex()).toString(); - QString currentId = m_settings->modelManagerSupportIdForMimeType(mimeType); - if (newId == currentId) - return false; + const QString currentCodeModelId + = settings.modelManagerSupportIdForMimeType(QLatin1String(Constants::CPP_SOURCE_MIMETYPE)); + return currentCodeModelId != settings.defaultId(); +} + +void CppCodeModelSettingsWidget::setupClangCodeModelWidgets() const +{ + bool isClangActive = false; + const bool isClangAvailable = m_settings->availableModelManagerSupportProvidersByName().size() > 1; + if (isClangAvailable) + isClangActive = isClangCodeModelActive(*m_settings.data()); + + m_ui->activateClangCodeModelPluginHint->setVisible(!isClangAvailable); + m_ui->clangSettingsGroupBox->setEnabled(isClangAvailable); + m_ui->clangSettingsGroupBox->setChecked(isClangActive); + + const QString extraClangOptions = m_settings->extraClangOptions().join(QLatin1Char('\n')); + m_ui->clangOptionsToAppendTextEdit->document()->setPlainText(extraClangOptions); +} + +bool CppCodeModelSettingsWidget::applyClangCodeModelWidgetsToSettings() const +{ + // Once the underlying settings are not mime type based anymore, simplify here. + // Until then, ensure that the settings are set uniformly for all the mime types + // to avoid surprises. + + const QString activeCodeModelId = m_ui->clangSettingsGroupBox->isChecked() + ? QLatin1String("ClangCodeMode.ClangCodeMode") + : QLatin1String("CppTools.BuiltinCodeModel"); + + foreach (const QString &mimeType, m_settings->supportedMimeTypes()) + m_settings->setModelManagerSupportIdForMimeType(mimeType, activeCodeModelId); + + const QString clangOptionsText = m_ui->clangOptionsToAppendTextEdit->document()->toPlainText(); + const QStringList extraClangOptions = clangOptionsText.split(QLatin1Char('\n'), + QString::SkipEmptyParts); + m_settings->setExtraClangOptions(extraClangOptions); - m_settings->setModelManagerSupportIdForMimeType(mimeType, newId); return true; } diff --git a/src/plugins/cpptools/cppcodemodelsettingspage.h b/src/plugins/cpptools/cppcodemodelsettingspage.h index 563d787f973..1e40a8c6faa 100644 --- a/src/plugins/cpptools/cppcodemodelsettingspage.h +++ b/src/plugins/cpptools/cppcodemodelsettingspage.h @@ -58,8 +58,8 @@ public: void applyToSettings() const; private: - bool applyToSettings(QComboBox *chooser, const QString &mimeType) const; - void applyToWidget(QComboBox *chooser, const QString &mimeType) const; + void setupClangCodeModelWidgets() const; + bool applyClangCodeModelWidgetsToSettings() const; private: Ui::CppCodeModelSettingsPage *m_ui; diff --git a/src/plugins/cpptools/cppcodemodelsettingspage.ui b/src/plugins/cpptools/cppcodemodelsettingspage.ui index 1109649ceb6..de7b381b165 100644 --- a/src/plugins/cpptools/cppcodemodelsettingspage.ui +++ b/src/plugins/cpptools/cppcodemodelsettingspage.ui @@ -15,74 +15,54 @@ - - - Code Completion and Semantic Highlighting + + + <i>Activate the Clang Code Model plugin to enable the options here.</i> - - - + + + + + + Use Clang Code Model + + + true + + + + - C + Append additional command line options to Clang, one per line. <i>Use this with care.</i> - - - - - 0 - 0 - - - + + - - - - C++ - - - - - - - - 0 - 0 - - - - - - - - Objective C - - - - - - - - - - Objective C++ - - - - - - - - - - Headers - - - - - + + + + + + Reset Options + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + diff --git a/src/plugins/cpptools/cppmodelmanager.cpp b/src/plugins/cpptools/cppmodelmanager.cpp index 70012ab682c..f82f414aeea 100644 --- a/src/plugins/cpptools/cppmodelmanager.cpp +++ b/src/plugins/cpptools/cppmodelmanager.cpp @@ -384,9 +384,9 @@ bool CppModelManager::replaceDocument(Document::Ptr newDoc) return true; } +/// Make sure that m_projectMutex is locked when calling this. void CppModelManager::ensureUpdated() { - QMutexLocker locker(&d->m_projectMutex); if (!d->m_dirty) return; @@ -475,8 +475,7 @@ void CppModelManager::dumpModelManagerConfiguration(const QString &logFileId) dumper.dumpProjectInfos(projectInfos()); dumper.dumpSnapshot(globalSnapshot, globalSnapshotTitle, /*isGlobalSnapshot=*/ true); dumper.dumpWorkingCopy(workingCopy()); - ensureUpdated(); - dumper.dumpMergedEntities(d->m_headerPaths, d->m_definedMacros); + dumper.dumpMergedEntities(headerPaths(), definedMacros()); } QSet CppModelManager::abstractEditorSupports() const @@ -941,14 +940,19 @@ QList CppModelManager::projectPartFromDependencies( return parts.values(); } -ProjectPart::Ptr CppModelManager::fallbackProjectPart() const +ProjectPart::Ptr CppModelManager::fallbackProjectPart() { ProjectPart::Ptr part(new ProjectPart); - part->projectDefines = d->m_definedMacros; - part->headerPaths = d->m_headerPaths; + part->projectDefines = definedMacros(); + part->headerPaths = headerPaths(); part->languageVersion = ProjectPart::CXX14; + + // Do not activate ObjectiveCExtensions since this will lead to the + // "objective-c++" language option for a project-less *.cpp file. part->languageExtensions = ProjectPart::AllExtensions; + part->languageExtensions &= ~ProjectPart::ObjectiveCExtensions; + part->qtVersion = ProjectPart::Qt5; part->updateLanguageFeatures(); @@ -1254,24 +1258,31 @@ CppIndexingSupport *CppModelManager::indexingSupport() QStringList CppModelManager::projectFiles() { + QMutexLocker locker(&d->m_projectMutex); ensureUpdated(); + return d->m_projectFiles; } ProjectPart::HeaderPaths CppModelManager::headerPaths() { + QMutexLocker locker(&d->m_projectMutex); ensureUpdated(); + return d->m_headerPaths; } void CppModelManager::setHeaderPaths(const ProjectPart::HeaderPaths &headerPaths) { + QMutexLocker locker(&d->m_projectMutex); d->m_headerPaths = headerPaths; } QByteArray CppModelManager::definedMacros() { + QMutexLocker locker(&d->m_projectMutex); ensureUpdated(); + return d->m_definedMacros; } diff --git a/src/plugins/cpptools/cppmodelmanager.h b/src/plugins/cpptools/cppmodelmanager.h index dd856582e59..e3f05640e0b 100644 --- a/src/plugins/cpptools/cppmodelmanager.h +++ b/src/plugins/cpptools/cppmodelmanager.h @@ -109,7 +109,7 @@ public: QList projectPartFromDependencies(const Utils::FileName &fileName) const; /// \return A synthetic \c ProjectPart which consists of all defines/includes/frameworks from /// all loaded projects. - ProjectPart::Ptr fallbackProjectPart() const; + ProjectPart::Ptr fallbackProjectPart(); CPlusPlus::Snapshot snapshot() const; Document::Ptr document(const QString &fileName) const; diff --git a/src/plugins/cpptools/cppprojects.cpp b/src/plugins/cpptools/cppprojects.cpp index 29f8d1cd8fe..d1ab05fb686 100644 --- a/src/plugins/cpptools/cppprojects.cpp +++ b/src/plugins/cpptools/cppprojects.cpp @@ -505,7 +505,7 @@ void ProjectPartBuilder::createProjectPart(const QStringList &theSources, } -CompilerOptionsBuilder::CompilerOptionsBuilder(const ProjectPart::Ptr &projectPart) +CompilerOptionsBuilder::CompilerOptionsBuilder(const ProjectPart &projectPart) : m_projectPart(projectPart) { } @@ -544,7 +544,7 @@ void CompilerOptionsBuilder::addHeaderPathOptions() QStringList result; - foreach (const HeaderPath &headerPath , m_projectPart->headerPaths) { + foreach (const HeaderPath &headerPath , m_projectPart.headerPaths) { if (headerPath.path.isEmpty()) continue; @@ -571,7 +571,7 @@ void CompilerOptionsBuilder::addHeaderPathOptions() void CompilerOptionsBuilder::addToolchainAndProjectDefines() { - QByteArray extendedDefines = m_projectPart->toolchainDefines + m_projectPart->projectDefines; + QByteArray extendedDefines = m_projectPart.toolchainDefines + m_projectPart.projectDefines; QStringList result; foreach (QByteArray def, extendedDefines.split('\n')) { @@ -645,7 +645,7 @@ static QStringList createLanguageOptionGcc(ProjectFile::Kind fileKind, bool objc void CompilerOptionsBuilder::addLanguageOption(ProjectFile::Kind fileKind) { - const bool objcExt = m_projectPart->languageExtensions & ProjectPart::ObjectiveCExtensions; + const bool objcExt = m_projectPart.languageExtensions & ProjectPart::ObjectiveCExtensions; const QStringList options = createLanguageOptionGcc(fileKind, objcExt); m_options.append(options); } @@ -653,9 +653,9 @@ void CompilerOptionsBuilder::addLanguageOption(ProjectFile::Kind fileKind) void CompilerOptionsBuilder::addOptionsForLanguage(bool checkForBorlandExtensions) { QStringList opts; - const ProjectPart::LanguageExtensions languageExtensions = m_projectPart->languageExtensions; + const ProjectPart::LanguageExtensions languageExtensions = m_projectPart.languageExtensions; const bool gnuExtensions = languageExtensions & ProjectPart::GnuExtensions; - switch (m_projectPart->languageVersion) { + switch (m_projectPart.languageVersion) { case ProjectPart::C89: opts << (gnuExtensions ? QLatin1String("-std=gnu89") : QLatin1String("-std=c89")); break; @@ -716,7 +716,7 @@ bool CompilerOptionsBuilder::excludeDefineLine(const QByteArray &defineLine) con // The right-hand sides are gcc built-ins that clang does not understand, and they'd // override clang's own (non-macro, it seems) definitions of the symbols on the left-hand // side. - const bool isGccToolchain = m_projectPart->toolchainType == ProjectExplorer::Constants::GCC_TOOLCHAIN_TYPEID; + const bool isGccToolchain = m_projectPart.toolchainType == ProjectExplorer::Constants::GCC_TOOLCHAIN_TYPEID; if (isGccToolchain && defineLine.contains("has_include")) return true; diff --git a/src/plugins/cpptools/cppprojects.h b/src/plugins/cpptools/cppprojects.h index c994257fa49..a63f5575875 100644 --- a/src/plugins/cpptools/cppprojects.h +++ b/src/plugins/cpptools/cppprojects.h @@ -215,7 +215,7 @@ private: class CPPTOOLS_EXPORT CompilerOptionsBuilder { public: - CompilerOptionsBuilder(const ProjectPart::Ptr &projectPart); + CompilerOptionsBuilder(const ProjectPart &projectPart); virtual ~CompilerOptionsBuilder() {} QStringList options() const; @@ -237,7 +237,7 @@ protected: virtual QString defineOption() const; virtual QString includeOption() const; - const ProjectPart::Ptr m_projectPart; + const ProjectPart m_projectPart; private: QString defineLineToDefineOption(const QByteArray &defineLine); @@ -247,4 +247,6 @@ private: } // namespace CppTools +Q_DECLARE_METATYPE(CppTools::ProjectPart::Ptr) + #endif // CPPPROJECTPART_H diff --git a/src/plugins/cpptools/cpptoolsconstants.h b/src/plugins/cpptools/cpptoolsconstants.h index d6130297f2a..819dde85fc5 100644 --- a/src/plugins/cpptools/cpptoolsconstants.h +++ b/src/plugins/cpptools/cpptoolsconstants.h @@ -55,6 +55,7 @@ enum { lowerCaseFilesDefault = 1 }; const char CPPTOOLS_SORT_EDITOR_DOCUMENT_OUTLINE[] = "SortedMethodOverview"; const char CPPTOOLS_MODEL_MANAGER_SUPPORTERS_KEY[] = "ModelManagerSupporters"; const char CPPTOOLS_MODEL_MANAGER_PCH_USAGE[] = "PCHUsage"; +const char CPPTOOLS_EXTRA_CLANG_OPTIONS[] = "ExtraClangOptions"; const char CPP_CODE_STYLE_SETTINGS_ID[] = "A.Cpp.Code Style"; const char CPP_CODE_STYLE_SETTINGS_NAME[] = QT_TRANSLATE_NOOP("CppTools", "Code Style"); diff --git a/src/plugins/cpptools/includeutils.cpp b/src/plugins/cpptools/includeutils.cpp index a50fb450714..fa4beb67864 100644 --- a/src/plugins/cpptools/includeutils.cpp +++ b/src/plugins/cpptools/includeutils.cpp @@ -391,7 +391,7 @@ QList IncludeGroup::detectIncludeGroupsByIncludeType(const QList result; - Client::IncludeType lastIncludeType; + Client::IncludeType lastIncludeType = Client::IncludeLocal; QList currentIncludes; bool isFirst = true; foreach (const Include &include, includes) { diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp index 1a69843726d..327bff8d213 100644 --- a/src/plugins/debugger/cdb/cdbengine.cpp +++ b/src/plugins/debugger/cdb/cdbengine.cpp @@ -340,8 +340,8 @@ void CdbEngine::syncOperateByInstruction(bool operateByInstruction) return; QTC_ASSERT(m_accessible, return); m_operateByInstruction = operateByInstruction; - runCommand(DebuggerCommand(m_operateByInstruction ? QByteArray("l-t") : QByteArray("l+t"))); - runCommand(DebuggerCommand(m_operateByInstruction ? QByteArray("l-s") : QByteArray("l+s"))); + runCommand({m_operateByInstruction ? "l-t" : "l+t", NoFlags}); + runCommand({m_operateByInstruction ? "l-s" : "l+s", NoFlags}); } void CdbEngine::syncVerboseLog(bool verboseLog) @@ -350,7 +350,7 @@ void CdbEngine::syncVerboseLog(bool verboseLog) return; QTC_ASSERT(m_accessible, return); m_verboseLog = verboseLog; - runCommand(DebuggerCommand(m_verboseLog ? QByteArray("!sym noisy") : QByteArray("!sym quiet"))); + runCommand({m_verboseLog ? "!sym noisy" : "!sym quiet", NoFlags}); } bool CdbEngine::canHandleToolTip(const DebuggerToolTipContext &context) const @@ -474,11 +474,9 @@ void CdbEngine::consoleStubExited() void CdbEngine::createFullBacktrace() { - DebuggerCommand cmd("~*kp"); - cmd.callback = [this](const DebuggerResponse &response) { + runCommand({"~*kp", BuiltinCommand, [this](const DebuggerResponse &response) { Internal::openTextEditor(QLatin1String("Backtrace $"), response.data.toLatin1()); - }; - runCommand(cmd, BuiltinCommand); + }}); } void CdbEngine::setupEngine() @@ -646,7 +644,7 @@ bool CdbEngine::launchCDB(const DebuggerRunParameters &sp, QString *errorMessage m_hasDebuggee = true; if (isRemote) { // We do not get an 'idle' in a remote session, but are accessible m_accessible = true; - runCommand(DebuggerCommand(".load " + extensionFileName.toLocal8Bit())); + runCommand({".load " + extensionFileName.toLocal8Bit(), NoFlags}); STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupOk") notifyEngineSetupOk(); } @@ -659,27 +657,25 @@ void CdbEngine::setupInferior() qDebug("setupInferior"); const DebuggerRunParameters &rp = runParameters(); if (!rp.commandsAfterConnect.isEmpty()) - runCommand(DebuggerCommand(rp.commandsAfterConnect)); + runCommand({rp.commandsAfterConnect, NoFlags}); // QmlCppEngine expects the QML engine to be connected before any breakpoints are hit // (attemptBreakpointSynchronization() will be directly called then) attemptBreakpointSynchronization(); if (rp.breakOnMain) { const BreakpointParameters bp(BreakpointAtMain); BreakpointModelId id(quint16(-1)); - DebuggerCommand cmd(cdbAddBreakpointCommand(bp, m_sourcePathMappings, id, true)); - cmd.callback = [this, id](const DebuggerResponse &r) { handleBreakInsert(r, id); }; - runCommand(cmd, BuiltinCommand); + runCommand({cdbAddBreakpointCommand(bp, m_sourcePathMappings, id, true), BuiltinCommand, + [this, id](const DebuggerResponse &r) { handleBreakInsert(r, id); }}); } - runCommand(DebuggerCommand("sxn 0x4000001f")); // Do not break on WowX86 exceptions. - runCommand(DebuggerCommand("sxn ibp")); // Do not break on initial breakpoints. - runCommand(DebuggerCommand(".asm source_line")); // Source line in assembly - runCommand(DebuggerCommand(m_extensionCommandPrefixBA + "setparameter maxStringLength=" + runCommand({"sxn 0x4000001f", NoFlags}); // Do not break on WowX86 exceptions. + runCommand({"sxn ibp", NoFlags}); // Do not break on initial breakpoints. + runCommand({".asm source_line", NoFlags}); // Source line in assembly + runCommand({m_extensionCommandPrefixBA + "setparameter maxStringLength=" + action(MaximalStringLength)->value().toByteArray() + " maxStackDepth=" - + action(MaximalStackDepth)->value().toByteArray())); + + action(MaximalStackDepth)->value().toByteArray(), NoFlags}); - DebuggerCommand cmd("pid"); - cmd.callback = [this](const DebuggerResponse &response) { + runCommand({"pid", ExtensionCommand, [this](const DebuggerResponse &response) { // Fails for core dumps. if (response.resultClass == ResultDone) notifyInferiorPid(response.data.data().toULongLong()); @@ -692,8 +688,7 @@ void CdbEngine::setupInferior() STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorSetupFailed") notifyInferiorSetupFailed(); } - }; - runCommand(cmd, ExtensionCommand); + }}); } static QByteArray msvcRunTime(const Abi::OSFlavor flavour) @@ -736,35 +731,25 @@ void CdbEngine::runEngine() const QStringList breakEvents = stringListSetting(CdbBreakEvents); foreach (const QString &breakEvent, breakEvents) - runCommand(DebuggerCommand(QByteArray("sxe ") + breakEvent.toLatin1())); + runCommand({"sxe " + breakEvent.toLatin1(), NoFlags}); // Break functions: each function must be fully qualified, // else the debugger will slow down considerably. - DebuggerCommand cmd; - cmd.callback = [this](const DebuggerResponse &r) { handleBreakInsert(r, BreakpointModelId()); }; + const auto cb = [this](const DebuggerResponse &r) { handleBreakInsert(r, BreakpointModelId()); }; if (boolSetting(CdbBreakOnCrtDbgReport)) { const QByteArray module = msvcRunTime(runParameters().toolChainAbi.osFlavor()); const QByteArray debugModule = module + 'D'; const QByteArray wideFunc = QByteArray(CdbOptionsPage::crtDbgReport).append('W'); - cmd.function = breakAtFunctionCommand(CdbOptionsPage::crtDbgReport, module); - runCommand(cmd, BuiltinCommand); - cmd.function = breakAtFunctionCommand(wideFunc, module); - runCommand(cmd, BuiltinCommand); - cmd.function = breakAtFunctionCommand(CdbOptionsPage::crtDbgReport, debugModule); - runCommand(cmd, BuiltinCommand); - cmd.function = breakAtFunctionCommand(wideFunc, debugModule); - runCommand(cmd, BuiltinCommand); + runCommand({breakAtFunctionCommand(CdbOptionsPage::crtDbgReport, module), BuiltinCommand, cb}); + runCommand({breakAtFunctionCommand(wideFunc, module), BuiltinCommand, cb}); + runCommand({breakAtFunctionCommand(CdbOptionsPage::crtDbgReport, debugModule), BuiltinCommand, cb}); } if (boolSetting(BreakOnWarning)) { - cmd.function = "bm /( QtCored4!qWarning"; // 'bm': All overloads. - runCommand(cmd, BuiltinCommand); - cmd.function = "bm /( Qt5Cored!QMessageLogger::warning"; - runCommand(cmd, BuiltinCommand); + runCommand({"bm /( QtCored4!qWarning", BuiltinCommand}); // 'bm': All overloads. + runCommand({"bm /( Qt5Cored!QMessageLogger::warning", BuiltinCommand}); } if (boolSetting(BreakOnFatal)) { - cmd.function = "bm /( QtCored4!qFatal"; // 'bm': All overloads. - runCommand(cmd, BuiltinCommand); - cmd.function = "bm /( Qt5Cored!QMessageLogger::fatal"; - runCommand(cmd, BuiltinCommand); + runCommand({"bm /( QtCored4!qFatal", BuiltinCommand}); // 'bm': All overloads. + runCommand({"bm /( Qt5Cored!QMessageLogger::fatal", BuiltinCommand}); } if (runParameters().startMode == AttachCore) { QTC_ASSERT(!m_coreStopReason.isNull(), return; ); @@ -848,10 +833,10 @@ void CdbEngine::shutdownEngine() detachDebugger(); // Remote requires a bit more force to quit. if (m_effectiveStartMode == AttachToRemoteServer) { - runCommand(DebuggerCommand(m_extensionCommandPrefixBA + "shutdownex")); - runCommand(DebuggerCommand("qq")); + runCommand({m_extensionCommandPrefixBA + "shutdownex", NoFlags}); + runCommand({"qq", NoFlags}); } else { - runCommand(DebuggerCommand("q")); + runCommand({"q", NoFlags}); } } else { // Remote process. No can do, currently @@ -885,7 +870,7 @@ void CdbEngine::processFinished() void CdbEngine::detachDebugger() { - runCommand(DebuggerCommand(".detach")); + runCommand({".detach", NoFlags}); } static inline bool isWatchIName(const QByteArray &iname) @@ -913,21 +898,21 @@ void CdbEngine::executeStep() { if (!m_operateByInstruction) m_sourceStepInto = true; // See explanation at handleStackTrace(). - runCommand(DebuggerCommand(QByteArray("t"))); // Step into-> t (trace) + runCommand({"t", NoFlags}); // Step into-> t (trace) STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested") notifyInferiorRunRequested(); } void CdbEngine::executeStepOut() { - runCommand(DebuggerCommand(QByteArray("gu"))); // Step out-> gu (go up) + runCommand({"gu", NoFlags}); // Step out-> gu (go up) STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested") notifyInferiorRunRequested(); } void CdbEngine::executeNext() { - runCommand(DebuggerCommand(QByteArray("p"))); // Step over -> p + runCommand({"p", NoFlags}); // Step over -> p STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested") notifyInferiorRunRequested(); } @@ -951,7 +936,7 @@ void CdbEngine::continueInferior() void CdbEngine::doContinueInferior() { - runCommand(DebuggerCommand(QByteArray("g"))); + runCommand({"g", NoFlags}); } bool CdbEngine::canInterruptInferior() const @@ -1025,9 +1010,8 @@ void CdbEngine::executeRunToLine(const ContextData &data) bp.lineNumber = data.lineNumber; } - DebuggerCommand cmd(cdbAddBreakpointCommand(bp, m_sourcePathMappings, BreakpointModelId(), true)); - cmd.callback = [this](const DebuggerResponse &r) { handleBreakInsert(r, BreakpointModelId()); }; - runCommand(cmd, BuiltinCommand); + runCommand({cdbAddBreakpointCommand(bp, m_sourcePathMappings, BreakpointModelId(), true), BuiltinCommand, + [this](const DebuggerResponse &r) { handleBreakInsert(r, BreakpointModelId()); }}); continueInferior(); } @@ -1036,10 +1020,8 @@ void CdbEngine::executeRunToFunction(const QString &functionName) // Add one-shot breakpoint BreakpointParameters bp(BreakpointByFunction); bp.functionName = functionName; - - DebuggerCommand cmd(cdbAddBreakpointCommand(bp, m_sourcePathMappings, BreakpointModelId(), true)); - cmd.callback = [this](const DebuggerResponse &r) { handleBreakInsert(r, BreakpointModelId()); }; - runCommand(cmd, BuiltinCommand); + runCommand({cdbAddBreakpointCommand(bp, m_sourcePathMappings, BreakpointModelId(), true), BuiltinCommand, + [this](const DebuggerResponse &r) { handleBreakInsert(r, BreakpointModelId()); }}); continueInferior(); } @@ -1049,7 +1031,7 @@ void CdbEngine::setRegisterValue(const QByteArray &name, const QString &value) QByteArray cmd; ByteArrayInputStream str(cmd); str << "r " << name << '=' << value; - runCommand(DebuggerCommand(cmd)); + runCommand({cmd, NoFlags}); reloadRegisters(); } @@ -1061,25 +1043,25 @@ void CdbEngine::executeJumpToLine(const ContextData &data) gotoLocation(Location(data.address)); } else { // Jump to source line: Resolve source line address and go to that location - DebuggerCommand cmd; - ByteArrayInputStream str(cmd.function); + QByteArray cmd; + ByteArrayInputStream str(cmd); str << "? `" << QDir::toNativeSeparators(data.fileName) << ':' << data.lineNumber << '`'; - cmd.callback = [this, data](const DebuggerResponse &r) { handleJumpToLineAddressResolution(r, data); }; - runCommand(cmd, BuiltinCommand); + runCommand({cmd, BuiltinCommand, [this, data](const DebuggerResponse &r) { + handleJumpToLineAddressResolution(r, data); }}); } } void CdbEngine::jumpToAddress(quint64 address) { // Fake a jump to address by setting the PC register. - QByteArray registerCmd; - ByteArrayInputStream str(registerCmd); + QByteArray cmd; + ByteArrayInputStream str(cmd); // PC-register depending on 64/32bit. str << "r " << (runParameters().toolChainAbi.wordWidth() == 64 ? "rip" : "eip") << '='; str.setHexPrefix(true); str.setIntegerBase(16); str << address; - runCommand(DebuggerCommand(registerCmd)); + runCommand({cmd, NoFlags}); } void CdbEngine::handleJumpToLineAddressResolution(const DebuggerResponse &response, const ContextData &context) @@ -1144,7 +1126,7 @@ void CdbEngine::assignValueInDebugger(WatchItem *w, const QString &expr, const Q break; } - runCommand(DebuggerCommand(cmd)); + runCommand({cmd, NoFlags}); // Update all locals in case we change a union or something pointed to // that affects other variables, too. updateLocals(); @@ -1168,11 +1150,11 @@ void CdbEngine::handleThreads(const DebuggerResponse &response) void CdbEngine::executeDebuggerCommand(const QString &command, DebuggerLanguages languages) { if (languages & CppLanguage) - runCommand(DebuggerCommand(command.toLocal8Bit())); + runCommand({command.toLocal8Bit(), NoFlags}); } // Post command to the cdb process -void CdbEngine::runCommand(const DebuggerCommand &dbgCmd, int flags) +void CdbEngine::runCommand(const DebuggerCommand &dbgCmd) { QByteArray cmd = dbgCmd.function + dbgCmd.argsToString(); if (!m_accessible) { @@ -1184,7 +1166,7 @@ void CdbEngine::runCommand(const DebuggerCommand &dbgCmd, int flags) QByteArray fullCmd; ByteArrayInputStream str(fullCmd); - if (flags & BuiltinCommand) { + if (dbgCmd.flags & BuiltinCommand) { // Post a built-in-command producing free-format output with a callback. // In order to catch the output, it is enclosed in 'echo' commands // printing a specially formatted token to be identifiable in the output. @@ -1192,7 +1174,7 @@ void CdbEngine::runCommand(const DebuggerCommand &dbgCmd, int flags) str << ".echo \"" << m_tokenPrefix << token << "<\"\n" << cmd << "\n.echo \"" << m_tokenPrefix << token << ">\""; m_commandForToken.insert(token, dbgCmd); - } else if (flags & ExtensionCommand) { + } else if (dbgCmd.flags & ExtensionCommand) { // Post an extension command producing one-line output with a callback, // pass along token for identification in hash. const int token = m_nextCommandToken++; @@ -1331,10 +1313,10 @@ void CdbEngine::doUpdateLocals(const UpdateParameters &updateParameters) if (partialUpdate) str << blankSeparator << updateParameters.partialVariable; - DebuggerCommand cmd("locals"); + DebuggerCommand cmd("locals", ExtensionCommand); cmd.args = QLatin1String(arguments); cmd.callback = [this, partialUpdate](const DebuggerResponse &r) { handleLocals(r, partialUpdate); }; - runCommand(cmd, ExtensionCommand); + runCommand(cmd); } void CdbEngine::updateAll() @@ -1349,9 +1331,8 @@ void CdbEngine::selectThread(ThreadId threadId) threadsHandler()->setCurrentThread(threadId); - DebuggerCommand cmd('~' + QByteArray::number(threadId.raw()) + " s"); - cmd.callback = [this](const DebuggerResponse &) { reloadFullStack(); }; - runCommand(cmd, BuiltinCommand); + runCommand({'~' + QByteArray::number(threadId.raw()) + " s", BuiltinCommand, + [this](const DebuggerResponse &) { reloadFullStack(); }}); } // Default address range for showing disassembly. @@ -1401,7 +1382,8 @@ void CdbEngine::postDisassemblerCommand(quint64 address, quint64 endAddress, // Parse: "00000000`77606060 cc int 3" agent->setContents(parseCdbDisassembler(response.data.data())); }; - runCommand(cmd, BuiltinCommand); + cmd.flags = BuiltinCommand; + runCommand(cmd); } void CdbEngine::postResolveSymbol(const QString &module, const QString &function, @@ -1413,14 +1395,10 @@ void CdbEngine::postResolveSymbol(const QString &module, const QString &function const QList addresses = m_symbolAddressCache.values(symbol); if (addresses.isEmpty()) { showMessage(QLatin1String("Resolving symbol: ") + symbol + QLatin1String("..."), LogMisc); - DebuggerCommand cmd(QByteArray("x ") + symbol.toLatin1()); - cmd.callback = [this, symbol, agent](const DebuggerResponse &r) { - handleResolveSymbol(r, symbol, agent); - }; - runCommand(cmd, BuiltinCommand); + runCommand({"x " + symbol.toLatin1(), BuiltinCommand, + [this, symbol, agent](const DebuggerResponse &r) { handleResolveSymbol(r, symbol, agent); }}); } else { - showMessage(QString::fromLatin1("Using cached addresses for %1."). - arg(symbol), LogMisc); + showMessage(QString::fromLatin1("Using cached addresses for %1.").arg(symbol), LogMisc); handleResolveSymbolHelper(addresses, agent); } } @@ -1556,7 +1534,7 @@ void CdbEngine::fetchMemory(MemoryAgent *agent, QObject *editor, quint64 addr, q void CdbEngine::postFetchMemory(const MemoryViewCookie &cookie) { - DebuggerCommand cmd("memory"); + DebuggerCommand cmd("memory", ExtensionCommand); QByteArray args; ByteArrayInputStream str(args); str << cookie.address << ' ' << cookie.length; @@ -1570,7 +1548,7 @@ void CdbEngine::postFetchMemory(const MemoryViewCookie &cookie) showMessage(response.data["msg"].toLatin1(), LogWarning); } }; - runCommand(cmd, ExtensionCommand); + runCommand(cmd); } void CdbEngine::changeMemory(Internal::MemoryAgent *, QObject *, quint64 addr, const QByteArray &data) @@ -1580,15 +1558,13 @@ void CdbEngine::changeMemory(Internal::MemoryAgent *, QObject *, quint64 addr, c const MemoryChangeCookie cookie(addr, data); doInterruptInferiorCustomSpecialStop(qVariantFromValue(cookie)); } else { - runCommand(DebuggerCommand(cdbWriteMemoryCommand(addr, data))); + runCommand({cdbWriteMemoryCommand(addr, data), NoFlags}); } } void CdbEngine::reloadModules() { - DebuggerCommand cmd("modules"); - cmd.callback = CB(handleModules); - runCommand(cmd, ExtensionCommand); + runCommand({"modules", ExtensionCommand, CB(handleModules)}); } void CdbEngine::loadSymbols(const QString & /* moduleName */) @@ -1607,9 +1583,7 @@ void CdbEngine::requestModuleSymbols(const QString &moduleName) void CdbEngine::reloadRegisters() { QTC_ASSERT(threadsHandler()->currentThreadIndex() >= 0, return); - DebuggerCommand cmd("registers"); - cmd.callback = CB(handleRegistersExt); - runCommand(cmd, ExtensionCommand); + runCommand({"registers", ExtensionCommand, CB(handleRegistersExt)}); } void CdbEngine::reloadSourceFiles() @@ -1620,18 +1594,18 @@ void CdbEngine::reloadFullStack() { if (debug) qDebug("%s", Q_FUNC_INFO); - DebuggerCommand cmd("stack"); + DebuggerCommand cmd("stack", ExtensionCommand); cmd.args = QStringLiteral("unlimited"); cmd.callback = CB(handleStackTrace); - runCommand(cmd, ExtensionCommand); + runCommand(cmd); } void CdbEngine::listBreakpoints() { - DebuggerCommand cmd("breakpoints"); + DebuggerCommand cmd("breakpoints", ExtensionCommand); cmd.args = QStringLiteral("-v"); cmd.callback = CB(handleBreakPoints); - runCommand(cmd, ExtensionCommand); + runCommand(cmd); } void CdbEngine::handleModules(const DebuggerResponse &response) @@ -1845,8 +1819,7 @@ unsigned CdbEngine::examineStopReason(const GdbMi &stopReason, if (!conditionalBreakPointTriggered && !parameters.condition.isEmpty()) { *message = msgCheckingConditionalBreakPoint(id, number, parameters.condition, QString::number(threadId)); - - DebuggerCommand cmd("expression"); + DebuggerCommand cmd("expression", ExtensionCommand); QByteArray args = parameters.condition; if (args.contains(' ') && !args.startsWith('"')) { args.prepend('"'); @@ -1856,7 +1829,7 @@ unsigned CdbEngine::examineStopReason(const GdbMi &stopReason, cmd.callback = [this, id, stopReason](const DebuggerResponse &response) { handleExpression(response, id, stopReason); }; - runCommand(cmd, ExtensionCommand); + runCommand(cmd); return StopReportLog; } @@ -2004,7 +1977,7 @@ void CdbEngine::processStop(const GdbMi &stopReason, bool conditionalBreakPointT // Start sequence to get all relevant data. if (stopFlags & StopInArtificialThread) { showMessage(tr("Switching to main thread..."), LogMisc); - runCommand(DebuggerCommand("~0 s")); + runCommand({"~0 s", NoFlags}); forcedThreadId = ThreadId(0); // Re-fetch stack again. reloadFullStack(); @@ -2019,9 +1992,8 @@ void CdbEngine::processStop(const GdbMi &stopReason, bool conditionalBreakPointT executeStepOut(); return; case ParseStackWow64: - DebuggerCommand cmd("lm m wow64"); - cmd.callback = [this, stack](const DebuggerResponse &r) { handleCheckWow64(r, stack); }; - runCommand(cmd, BuiltinCommand); + runCommand({"lm m wow64", BuiltinCommand, + [this, stack](const DebuggerResponse &r) { handleCheckWow64(r, stack); }}); break; } } else { @@ -2112,9 +2084,8 @@ void CdbEngine::handleCheckWow64(const DebuggerResponse &response, const GdbMi & // start end module name // 00000000`77490000 00000000`774d5000 wow64 (deferred) if (response.data.data().contains("wow64")) { - DebuggerCommand cmd("k"); - cmd.callback = [this, stack](const DebuggerResponse &r) { ensureUsing32BitStackInWow64(r, stack); }; - runCommand(cmd, BuiltinCommand); + runCommand({"k", BuiltinCommand, + [this, stack](const DebuggerResponse &r) { ensureUsing32BitStackInWow64(r, stack); }}); return; } m_wow64State = noWow64Stack; @@ -2134,9 +2105,7 @@ void CdbEngine::ensureUsing32BitStackInWow64(const DebuggerResponse &response, c return; } else if (line.startsWith("Child-SP")) { m_wow64State = wow64Stack64Bit; - DebuggerCommand cmd("!wow64exts.sw"); - cmd.callback = CB(handleSwitchWow64Stack); - runCommand(cmd, BuiltinCommand); + runCommand({"!wow64exts.sw", BuiltinCommand, CB(handleSwitchWow64Stack)}); return; } } @@ -2153,9 +2122,7 @@ void CdbEngine::handleSwitchWow64Stack(const DebuggerResponse &response) else m_wow64State = noWow64Stack; // reload threads and the stack after switching the mode - DebuggerCommand cmd("threads"); - cmd.callback = CB(handleThreads); - runCommand(cmd, ExtensionCommand); + runCommand({"threads", ExtensionCommand, CB(handleThreads)}); } void CdbEngine::handleSessionAccessible(unsigned long cdbExState) @@ -2670,6 +2637,7 @@ void CdbEngine::attemptBreakpointSynchronization() foreach (Breakpoint bp, bps) { BreakpointParameters parameters = bp.parameters(); BreakpointModelId id = bp.id(); + const auto handleBreakInsertCB = [this, id](const DebuggerResponse &r) { handleBreakInsert(r, id); }; BreakpointResponse response; response.fromParameters(parameters); response.id = BreakpointResponseId(id.majorPart(), id.minorPart()); @@ -2688,16 +2656,14 @@ void CdbEngine::attemptBreakpointSynchronization() lineCorrection.reset(new BreakpointCorrectionContext(Internal::cppCodeModelSnapshot(), CppTools::CppModelManager::instance()->workingCopy())); response.lineNumber = lineCorrection->fixLineNumber(parameters.fileName, parameters.lineNumber); - DebuggerCommand cmd(cdbAddBreakpointCommand(response, m_sourcePathMappings, id, false)); - cmd.callback = [this, id](const DebuggerResponse &r) { handleBreakInsert(r, id); }; - runCommand(cmd, BuiltinCommand); + QByteArray cmd = cdbAddBreakpointCommand(response, m_sourcePathMappings, id, false); + runCommand({cmd, BuiltinCommand, handleBreakInsertCB}); } else { - DebuggerCommand cmd(cdbAddBreakpointCommand(parameters, m_sourcePathMappings, id, false)); - cmd.callback = [this, id](const DebuggerResponse &r) { handleBreakInsert(r, id); }; - runCommand(cmd, BuiltinCommand); + QByteArray cmd = cdbAddBreakpointCommand(parameters, m_sourcePathMappings, id, false); + runCommand({cmd, BuiltinCommand, handleBreakInsertCB}); } if (!parameters.enabled) - runCommand(DebuggerCommand("bd " + QByteArray::number(breakPointIdToCdbId(id)))); + runCommand({"bd " + QByteArray::number(breakPointIdToCdbId(id)), NoFlags}); bp.notifyBreakpointInsertProceeding(); bp.notifyBreakpointInsertOk(); m_pendingBreakpointMap.insert(id, response); @@ -2716,24 +2682,23 @@ void CdbEngine::attemptBreakpointSynchronization() qPrintable(parameters.toString())); if (parameters.enabled != bp.response().enabled) { // Change enabled/disabled breakpoints without triggering update. - runCommand(DebuggerCommand((parameters.enabled ? "be " : "bd ") - + QByteArray::number(breakPointIdToCdbId(id)))); + runCommand({(parameters.enabled ? "be " : "bd ") + + QByteArray::number(breakPointIdToCdbId(id)), NoFlags}); response.pending = false; response.enabled = parameters.enabled; bp.setResponse(response); } else { // Delete and re-add, triggering update addedChanged = true; - runCommand(DebuggerCommand(cdbClearBreakpointCommand(id))); - DebuggerCommand cmd(cdbAddBreakpointCommand(parameters, m_sourcePathMappings, id, false)); - cmd.callback = [this, id](const DebuggerResponse &r) { handleBreakInsert(r, id); }; - runCommand(cmd, BuiltinCommand); + runCommand({cdbClearBreakpointCommand(id), NoFlags}); + QByteArray cmd(cdbAddBreakpointCommand(parameters, m_sourcePathMappings, id, false)); + runCommand({cmd, BuiltinCommand, handleBreakInsertCB}); m_pendingBreakpointMap.insert(id, response); } bp.notifyBreakpointChangeOk(); break; case BreakpointRemoveRequested: - runCommand(DebuggerCommand(cdbClearBreakpointCommand(id))); + runCommand({cdbClearBreakpointCommand(id), NoFlags}); bp.notifyBreakpointRemoveProceeding(); bp.notifyBreakpointRemoveOk(); m_pendingBreakpointMap.remove(id); @@ -2745,7 +2710,7 @@ void CdbEngine::attemptBreakpointSynchronization() foreach (BreakpointModelId id, m_insertSubBreakpointMap.keys()) { addedChanged = true; const BreakpointResponse &response = m_insertSubBreakpointMap.value(id); - runCommand(DebuggerCommand(cdbAddBreakpointCommand(response, m_sourcePathMappings, id, false))); + runCommand({cdbAddBreakpointCommand(response, m_sourcePathMappings, id, false), NoFlags}); m_insertSubBreakpointMap.remove(id); m_pendingSubBreakpointMap.insert(id, response); } @@ -2873,9 +2838,7 @@ unsigned CdbEngine::parseStackTrace(const GdbMi &data, bool sourceStepInto) void CdbEngine::loadAdditionalQmlStack() { - DebuggerCommand cmd("qmlstack"); - cmd.callback = CB(handleAdditionalQmlStack); - runCommand(cmd, ExtensionCommand); + runCommand({"qmlstack", ExtensionCommand, CB(handleAdditionalQmlStack)}); } void CdbEngine::handleAdditionalQmlStack(const DebuggerResponse &response) @@ -2920,9 +2883,8 @@ void CdbEngine::handleStackTrace(const DebuggerResponse &response) GdbMi stack = response.data; if (response.resultClass == ResultDone) { if (parseStackTrace(stack, false) == ParseStackWow64) { - DebuggerCommand cmd("lm m wow64"); - cmd.callback = [this, stack](const DebuggerResponse &r) { handleCheckWow64(r, stack); }; - runCommand(cmd, BuiltinCommand); + runCommand({"lm m wow64", BuiltinCommand, + [this, stack](const DebuggerResponse &r) { handleCheckWow64(r, stack); }}); } } else { showMessage(stack["msg"].toLatin1(), LogError); @@ -3096,17 +3058,16 @@ void CdbEngine::watchPoint(const QPoint &p) void CdbEngine::postWidgetAtCommand() { - DebuggerCommand cmd("widgetat"); + DebuggerCommand cmd("widgetat", ExtensionCommand); cmd.args = QString::fromLatin1("%1 %2").arg(m_watchPointX, m_watchPointY); - cmd.callback = CB(handleWidgetAt); - runCommand(cmd, ExtensionCommand); + runCommand(cmd); } void CdbEngine::handleCustomSpecialStop(const QVariant &v) { if (v.canConvert()) { const MemoryChangeCookie changeData = qvariant_cast(v); - runCommand(DebuggerCommand(cdbWriteMemoryCommand(changeData.address, changeData.data))); + runCommand({cdbWriteMemoryCommand(changeData.address, changeData.data), NoFlags}); return; } if (v.canConvert()) { diff --git a/src/plugins/debugger/cdb/cdbengine.h b/src/plugins/debugger/cdb/cdbengine.h index 9623e4eb781..8e677957617 100644 --- a/src/plugins/debugger/cdb/cdbengine.h +++ b/src/plugins/debugger/cdb/cdbengine.h @@ -127,7 +127,7 @@ private slots: void readyReadStandardError(); void processError(); void processFinished(); - void runCommand(const DebuggerCommand &cmd, int flags = 0); + void runCommand(const DebuggerCommand &cmd) override; void operateByInstructionTriggered(bool); void verboseLogTriggered(bool); @@ -164,7 +164,7 @@ private: ParseStackWow64 = 3 // Hit on a frame with 32bit emulation, switch debugger to 32 bit mode }; enum CommandFlags { - NoCallBack = 0, + NoFlags = 0, BuiltinCommand, ExtensionCommand, }; diff --git a/src/plugins/debugger/commonoptionspage.cpp b/src/plugins/debugger/commonoptionspage.cpp index aada5d7c33b..9dd3eaabbaf 100644 --- a/src/plugins/debugger/commonoptionspage.cpp +++ b/src/plugins/debugger/commonoptionspage.cpp @@ -102,10 +102,6 @@ CommonOptionsPageWidget::CommonOptionsPageWidget checkBoxUseToolTipsInMainEditor = new QCheckBox(behaviorBox); checkBoxUseToolTipsInMainEditor->setText(tr("Use tooltips in main editor while debugging")); - checkBoxListSourceFiles = new QCheckBox(behaviorBox); - checkBoxListSourceFiles->setToolTip(tr("Populates the source file view automatically. This might slow down debugger startup considerably.")); - checkBoxListSourceFiles->setText(tr("Populate source file view automatically")); - QString t = tr("Stopping and stepping in the debugger " "will automatically open views associated with the current location.") + QLatin1Char('\n'); checkBoxCloseSourceBuffersOnExit = new QCheckBox(behaviorBox); @@ -174,11 +170,10 @@ CommonOptionsPageWidget::CommonOptionsPageWidget gridLayout->addLayout(horizontalLayout, 7, 0, 1, 2); gridLayout->addWidget(checkBoxFontSizeFollowsEditor, 0, 1, 1, 1); - gridLayout->addWidget(checkBoxListSourceFiles, 1, 1, 1, 1); - gridLayout->addWidget(checkBoxSwitchModeOnExit, 2, 1, 1, 1); - gridLayout->addWidget(checkBoxShowQmlObjectTree, 3, 1, 1, 1); - gridLayout->addWidget(checkBoxKeepEditorStationaryWhileStepping, 4, 1, 1, 1); - gridLayout->addWidget(checkBoxRegisterForPostMortem, 5, 1, 1, 1); + gridLayout->addWidget(checkBoxSwitchModeOnExit, 1, 1, 1, 1); + gridLayout->addWidget(checkBoxShowQmlObjectTree, 2, 1, 1, 1); + gridLayout->addWidget(checkBoxKeepEditorStationaryWhileStepping, 3, 1, 1, 1); + gridLayout->addWidget(checkBoxRegisterForPostMortem, 4, 1, 1, 1); QVBoxLayout *verticalLayout = new QVBoxLayout(this); verticalLayout->addWidget(behaviorBox); @@ -187,8 +182,6 @@ CommonOptionsPageWidget::CommonOptionsPageWidget m_group->clear(); - m_group->insert(action(ListSourceFiles), - checkBoxListSourceFiles); m_group->insert(action(UseAlternatingRowColors), checkBoxUseAlternatingRowColors); m_group->insert(action(UseToolTipsInMainEditor), diff --git a/src/plugins/debugger/debuggeractions.cpp b/src/plugins/debugger/debuggeractions.cpp index 0b04200725a..dd2bd488c29 100644 --- a/src/plugins/debugger/debuggeractions.cpp +++ b/src/plugins/debugger/debuggeractions.cpp @@ -556,13 +556,6 @@ DebuggerSettings::DebuggerSettings() item->setCheckable(true); item->setDefaultValue(false); insertItem(UseAddressInStackView, item); - item = new SavedAction(this); - - item->setSettingsKey(debugModeGroup, QLatin1String("ListSourceFiles")); - item->setText(tr("List Source Files")); - item->setCheckable(true); - item->setDefaultValue(false); - insertItem(ListSourceFiles, item); item = new SavedAction(this); item->setSettingsKey(debugModeGroup, QLatin1String("SkipKnownFrames")); diff --git a/src/plugins/debugger/debuggeractions.h b/src/plugins/debugger/debuggeractions.h index c7dabd7015c..a1fd3962807 100644 --- a/src/plugins/debugger/debuggeractions.h +++ b/src/plugins/debugger/debuggeractions.h @@ -162,9 +162,6 @@ enum DebuggerActionCode MaximalStringLength, DisplayStringLimit, - // Source List - ListSourceFiles, - // Running SkipKnownFrames, EnableReverseDebugging, diff --git a/src/plugins/debugger/debuggerengine.cpp b/src/plugins/debugger/debuggerengine.cpp index 2d5f14c0ebb..2084bf64a57 100644 --- a/src/plugins/debugger/debuggerengine.cpp +++ b/src/plugins/debugger/debuggerengine.cpp @@ -1479,6 +1479,12 @@ void DebuggerEngine::watchPoint(const QPoint &) { } +void DebuggerEngine::runCommand(const DebuggerCommand &) +{ + // Overridden in the engines that use the interface. + QTC_CHECK(false); +} + void DebuggerEngine::fetchDisassembler(DisassemblerAgent *) { } diff --git a/src/plugins/debugger/debuggerengine.h b/src/plugins/debugger/debuggerengine.h index b4edd5eeaa1..310b1684371 100644 --- a/src/plugins/debugger/debuggerengine.h +++ b/src/plugins/debugger/debuggerengine.h @@ -33,6 +33,7 @@ #include "debugger_global.h" #include "debuggerconstants.h" +#include "debuggerprotocol.h" #include "debuggerstartparameters.h" #include @@ -61,7 +62,6 @@ namespace Internal { class DebuggerEnginePrivate; class DebuggerPluginPrivate; class DisassemblerAgent; -class GdbMi; class MemoryAgent; class WatchData; class WatchItem; @@ -98,7 +98,6 @@ public: QString coreFile; QString overrideStartScript; // Used in attach to core and remote debugging QString startMessage; // First status message shown. - QMap sourcePathMap; QString debugInfoLocation; // Gdb "set-debug-file-directory". QStringList debugSourceLocation; // Gdb "directory" QString serverStartScript; @@ -217,6 +216,7 @@ public: MemoryView = 0x4 //!< Open a separate view (using the pos-parameter). }; + virtual void runCommand(const DebuggerCommand &cmd); virtual void openMemoryView(const MemoryViewSetupData &data); virtual void fetchMemory(Internal::MemoryAgent *, QObject *, quint64 addr, quint64 length); diff --git a/src/plugins/debugger/debuggerprotocol.h b/src/plugins/debugger/debuggerprotocol.h index 6c9dbebeb4e..f85746a006a 100644 --- a/src/plugins/debugger/debuggerprotocol.h +++ b/src/plugins/debugger/debuggerprotocol.h @@ -52,9 +52,10 @@ public: typedef std::function Callback; DebuggerCommand() {} - DebuggerCommand(const char *f, int fl = 0) : function(f), flags(fl) {} - DebuggerCommand(const QByteArray &f, int fl = 0) : function(f), flags(fl) {} - DebuggerCommand(const char *f, const QJsonValue &a, int fl = 0) : function(f), args(a), flags(fl) {} + DebuggerCommand(const QByteArray &f) : function(f), flags(0) {} + DebuggerCommand(const QByteArray &f, const QJsonValue &a) : function(f), args(a), flags(0) {} + DebuggerCommand(const QByteArray &f, int fl) : function(f), flags(fl) {} + DebuggerCommand(const QByteArray &f, int fl, const Callback &cb) : function(f), callback(cb), flags(fl) {} void arg(const char *value); void arg(const char *name, int value); diff --git a/src/plugins/debugger/debuggerstartparameters.h b/src/plugins/debugger/debuggerstartparameters.h index ac680ed896f..a7b75830e58 100644 --- a/src/plugins/debugger/debuggerstartparameters.h +++ b/src/plugins/debugger/debuggerstartparameters.h @@ -95,6 +95,9 @@ public: QSsh::SshConnectionParameters connParams; bool remoteSetupNeeded = false; + // Used by Mer plugin (3rd party) + QMap sourcePathMap; + // Used by baremetal plugin QByteArray commandsForReset; // commands used for resetting the inferior bool useContinueInsteadOfRun = false; // if connected to a hw debugger run is not possible but continue is used diff --git a/src/plugins/debugger/gdb/attachgdbadapter.cpp b/src/plugins/debugger/gdb/attachgdbadapter.cpp index bdd71bc4bd9..56ac21f3bc3 100644 --- a/src/plugins/debugger/gdb/attachgdbadapter.cpp +++ b/src/plugins/debugger/gdb/attachgdbadapter.cpp @@ -75,9 +75,8 @@ void GdbAttachEngine::runEngine() { QTC_ASSERT(state() == EngineRunRequested, qDebug() << state()); const qint64 pid = runParameters().attachPID; - DebuggerCommand cmd("attach " + QByteArray::number(pid)); - cmd.callback = [this](const DebuggerResponse &r) { handleAttach(r); }; - runCommand(cmd); + runCommand({"attach " + QByteArray::number(pid), NoFlags, + [this](const DebuggerResponse &r) { handleAttach(r); }}); showStatusMessage(tr("Attached to process %1.").arg(inferiorPid())); } diff --git a/src/plugins/debugger/gdb/coregdbadapter.cpp b/src/plugins/debugger/gdb/coregdbadapter.cpp index d07cf027271..8987b9e879f 100644 --- a/src/plugins/debugger/gdb/coregdbadapter.cpp +++ b/src/plugins/debugger/gdb/coregdbadapter.cpp @@ -212,9 +212,8 @@ void GdbCoreEngine::setupInferior() // Do that first, otherwise no symbols are loaded. QFileInfo fi(m_executable); QByteArray path = fi.absoluteFilePath().toLocal8Bit(); - DebuggerCommand cmd("-file-exec-and-symbols \"" + path + '"'); - cmd.callback = CB(handleFileExecAndSymbols); - runCommand(cmd); + runCommand({"-file-exec-and-symbols \"" + path + '"', NoFlags, + CB(handleFileExecAndSymbols)}); } void GdbCoreEngine::handleFileExecAndSymbols(const DebuggerResponse &response) @@ -237,9 +236,8 @@ void GdbCoreEngine::handleFileExecAndSymbols(const DebuggerResponse &response) void GdbCoreEngine::runEngine() { CHECK_STATE(EngineRunRequested); - DebuggerCommand cmd("target core " + coreFileName().toLocal8Bit()); - cmd.callback = CB(handleTargetCore); - runCommand(cmd); + runCommand({"target core " + coreFileName().toLocal8Bit(), NoFlags, + CB(handleTargetCore)}); } void GdbCoreEngine::handleTargetCore(const DebuggerResponse &response) @@ -252,7 +250,7 @@ void GdbCoreEngine::handleTargetCore(const DebuggerResponse &response) // symbols yet. Load them in order of importance. reloadStack(); reloadModulesInternal(); - runCommand("p 5", CB(handleRoundTrip)); + runCommand({"p 5", NoFlags, CB(handleRoundTrip)}); return; } showStatusMessage(tr("Attach to core \"%1\" failed:").arg(runParameters().coreFile) diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index 5621613c2fd..1dbe494d462 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -735,21 +735,19 @@ void GdbEngine::handleAsyncOutput(const QByteArray &asyncClass, const GdbMi &res foreach (const GdbMi &bkpt, res.children()) { const QByteArray nr = bkpt["number"].data(); BreakpointResponseId rid(nr); - if (!isHiddenBreakpoint(rid)) { - if (nr.contains('.')) { - // A sub-breakpoint. - BreakpointResponse sub; - updateResponse(sub, bkpt); - sub.id = rid; - sub.type = br.type; - bp.insertSubBreakpoint(sub); - } else { - // A primary breakpoint. - bp = handler->findBreakpointByResponseId(rid); - br = bp.response(); - updateResponse(br, bkpt); - bp.setResponse(br); - } + if (nr.contains('.')) { + // A sub-breakpoint. + BreakpointResponse sub; + updateResponse(sub, bkpt); + sub.id = rid; + sub.type = br.type; + bp.insertSubBreakpoint(sub); + } else { + // A primary breakpoint. + bp = handler->findBreakpointByResponseId(rid); + br = bp.response(); + updateResponse(br, bkpt); + bp.setResponse(br); } } } else if (asyncClass == "breakpoint-created") { @@ -851,7 +849,7 @@ void GdbEngine::interruptInferior() return; if (usesExecInterrupt()) { - runCommand("-exec-interrupt"); + runCommand({"-exec-interrupt", NoFlags}); } else { showStatusMessage(tr("Stop requested..."), 5000); showMessage(_("TRYING TO INTERRUPT INFERIOR")); @@ -898,20 +896,6 @@ void GdbEngine::maybeHandleInferiorPidChanged(const QString &pid0) notifyInferiorPid(pid); } -void GdbEngine::runCommand(const QByteArray &command, const DebuggerCommand::Callback &callback, int flags) -{ - DebuggerCommand cmd(command); - cmd.callback = callback; - cmd.flags = flags; - runCommand(cmd); -} - -void GdbEngine::runCommand(const QByteArray &command, int flags) -{ - DebuggerCommand cmd(command, flags); - runCommand(cmd); -} - void GdbEngine::runCommand(const DebuggerCommand &command) { const int token = ++currentToken(); @@ -1227,7 +1211,7 @@ void GdbEngine::executeDebuggerCommand(const QString &command, DebuggerLanguages if (!(languages & CppLanguage)) return; QTC_CHECK(acceptsDebuggerCommands()); - runCommand(command.toLatin1()); + runCommand({command.toLatin1(), NoFlags}); } // This is triggered when switching snapshots. @@ -1240,7 +1224,7 @@ void GdbEngine::updateAll() cmd.callback = [this](const DebuggerResponse &r) { handleStackListFrames(r, false); }; runCommand(cmd); stackHandler()->setCurrentIndex(0); - runCommand("-thread-info", CB(handleThreadInfo)); + runCommand({"-thread-info", NoFlags, CB(handleThreadInfo)}); reloadRegisters(); updateLocals(); } @@ -1386,9 +1370,8 @@ void GdbEngine::handleStopResponse(const GdbMi &data) bool gotoHandleStop1 = true; if (!m_fullStartDone) { m_fullStartDone = true; - DebuggerCommand cmd("sharedlibrary .*"); - cmd.callback = [this, data](const DebuggerResponse &) { handleStop1(data); }; - runCommand(cmd); + runCommand({"sharedlibrary .*", NoFlags, + [this, data](const DebuggerResponse &) { handleStop1(data); }}); gotoHandleStop1 = false; } @@ -1413,7 +1396,7 @@ void GdbEngine::handleStopResponse(const GdbMi &data) showMessage(_("INVALID STOPPED REASON"), LogWarning); } - if (rid.isValid() && frame.isValid() && !isQFatalBreakpoint(rid)) { + if (rid.isValid() && frame.isValid()) { // Use opportunity to update the breakpoint marker position. Breakpoint bp = breakHandler()->findBreakpointByResponseId(rid); const BreakpointResponse &response = bp.response(); @@ -1430,7 +1413,6 @@ void GdbEngine::handleStopResponse(const GdbMi &data) // Quickly set the location marker. if (lineNumber && !boolSetting(OperateByInstruction) && QFileInfo::exists(fullName) - && !isQFatalBreakpoint(rid) && function != "qt_v4TriggeredBreakpointHook" && function != "qt_qmlDebugMessageAvailable" && language != "js") @@ -1544,9 +1526,9 @@ void GdbEngine::handleStop1(const GdbMi &data) if (!m_systemDumpersLoaded) { m_systemDumpersLoaded = true; if (m_gdbVersion >= 70400 && boolSetting(LoadGdbDumpers)) - runCommand("importPlainDumpers on"); + runCommand({"importPlainDumpers on", NoFlags}); else - runCommand("importPlainDumpers off"); + runCommand({"importPlainDumpers off", NoFlags}); } handleStop2(data); @@ -1701,12 +1683,12 @@ void GdbEngine::handleShowVersion(const DebuggerResponse &response) .arg(gdbBuildVersion).arg(_(isMacGdb ? " (APPLE)" : ""))); if (usesExecInterrupt()) - runCommand("set target-async on", ConsoleCommand); + runCommand({"set target-async on", ConsoleCommand}); else - runCommand("set target-async off", ConsoleCommand); + runCommand({"set target-async off", ConsoleCommand}); if (runParameters().multiProcess) - runCommand("set detach-on-fork off", ConsoleCommand); + runCommand({"set detach-on-fork off", ConsoleCommand}); //runCommand("set build-id-verbose 2", ConsoleCommand); } @@ -1892,8 +1874,8 @@ void GdbEngine::notifyAdapterShutdownOk() switch (m_gdbProc.state()) { case QProcess::Running: { if (runParameters().closeMode == KillAndExitMonitorAtClose) - runCommand("monitor exit"); - runCommand("exitGdb", CB(handleGdbExit), ExitRequest|PythonCommand); + runCommand({"monitor exit", NoFlags}); + runCommand({"exitGdb", ExitRequest|PythonCommand, CB(handleGdbExit)}); break; } case QProcess::NotRunning: @@ -2093,20 +2075,19 @@ void GdbEngine::executeStepI() void GdbEngine::executeStepOut() { CHECK_STATE(InferiorStopOk); - runCommand("-stack-select-frame 0", Discardable); + runCommand({"-stack-select-frame 0", Discardable}); setTokenBarrier(); notifyInferiorRunRequested(); showStatusMessage(tr("Finish function requested..."), 5000); if (isNativeMixedActiveFrame()) { - DebuggerCommand cmd("executeStepOut", RunRequest|PythonCommand); - runCommand(cmd); + runCommand({"executeStepOut", RunRequest|PythonCommand}); } else { - runCommand("-exec-finish", CB(handleExecuteContinue), RunRequest); + runCommand({"-exec-finish", RunRequest, CB(handleExecuteContinue)}); // -exec-finish in 'main' results (correctly) in // 40^error,msg="\"finish\" not meaningful in the outermost frame." // However, this message does not seem to get flushed before // anything else happen - i.e. "never". Force some extra output. - runCommand("print 32"); + runCommand({"print 32"}); } } @@ -2186,11 +2167,9 @@ void GdbEngine::executeRunToLine(const ContextData &data) else loc = '"' + breakLocation(data.fileName).toLocal8Bit() + '"' + ':' + QByteArray::number(data.lineNumber); - runCommand("tbreak " + loc); + runCommand({"tbreak " + loc, NoFlags}); - DebuggerCommand cmd("continue", RunRequest); - cmd.callback = CB(handleExecuteRunToLine); - runCommand(cmd); + runCommand({"continue", RunRequest, CB(handleExecuteRunToLine)}); #else // Seems to jump to unpredicatable places. Observed in the manual // tests in the Foo::Foo() constructor with both gdb 6.8 and 7.1. @@ -2204,7 +2183,7 @@ void GdbEngine::executeRunToFunction(const QString &functionName) { CHECK_STATE(InferiorStopOk); setTokenBarrier(); - runCommand("-break-insert -t " + functionName.toLatin1()); + runCommand({"-break-insert -t " + functionName.toLatin1(), NoFlags}); showStatusMessage(tr("Run to function %1 requested...").arg(functionName), 5000); continueInferiorInternal(); } @@ -2218,12 +2197,10 @@ void GdbEngine::executeJumpToLine(const ContextData &data) else loc = '"' + breakLocation(data.fileName).toLocal8Bit() + '"' + ':' + QByteArray::number(data.lineNumber); - runCommand("tbreak " + loc); + runCommand({"tbreak " + loc, NoFlags}); notifyInferiorRunRequested(); - DebuggerCommand cmd("jump" + loc, RunRequest); - cmd.callback = CB(handleExecuteJumpToLine); - runCommand(cmd); + runCommand({"jump" + loc, RunRequest, CB(handleExecuteJumpToLine)}); // will produce something like // &"jump \"/home/apoenitz/dev/work/test1/test1.cpp\":242" // ~"Continuing at 0x4058f3." @@ -2238,7 +2215,7 @@ void GdbEngine::executeReturn() setTokenBarrier(); notifyInferiorRunRequested(); showStatusMessage(tr("Immediate return from function requested..."), 5000); - runCommand("-exec-finish", CB(handleExecuteReturn), RunRequest); + runCommand({"-exec-finish", RunRequest, CB(handleExecuteReturn)}); } void GdbEngine::handleExecuteReturn(const DebuggerResponse &response) @@ -2572,18 +2549,13 @@ void GdbEngine::handleBreakInsert1(const DebuggerResponse &response, Breakpoint // the "main" entry. Use the "main" entry to retrieve the // already known data from the BreakpointManager, and then // iterate over all items to update main- and sub-data. - const GdbMi mainbkpt = response.data["bkpt"]; - const QByteArray mainnr = mainbkpt["number"].data(); - const BreakpointResponseId mainrid(mainnr); - if (!isHiddenBreakpoint(mainrid)) { - foreach (const GdbMi &bkpt, response.data.children()) - handleBkpt(bkpt, bp); - if (bp.needsChange()) { - bp.notifyBreakpointChangeAfterInsertNeeded(); - changeBreakpoint(bp); - } else { - bp.notifyBreakpointInsertOk(); - } + foreach (const GdbMi &bkpt, response.data.children()) + handleBkpt(bkpt, bp); + if (bp.needsChange()) { + bp.notifyBreakpointChangeAfterInsertNeeded(); + changeBreakpoint(bp); + } else { + bp.notifyBreakpointInsertOk(); } } else if (response.data["msg"].data().contains("Unknown option")) { // Older version of gdb don't know the -a option to set tracepoints @@ -2891,7 +2863,7 @@ void GdbEngine::removeBreakpoint(Breakpoint bp) void GdbEngine::loadSymbols(const QString &modulePath) { // FIXME: gdb does not understand quoted names here (tested with 6.8) - runCommand("sharedlibrary " + dotEscape(modulePath.toLocal8Bit())); + runCommand({"sharedlibrary " + dotEscape(modulePath.toLocal8Bit()), NoFlags}); reloadModulesInternal(); reloadStack(); updateLocals(); @@ -2899,7 +2871,7 @@ void GdbEngine::loadSymbols(const QString &modulePath) void GdbEngine::loadAllSymbols() { - runCommand("sharedlibrary .*"); + runCommand({"sharedlibrary .*", NoFlags}); reloadModulesInternal(); reloadStack(); updateLocals(); @@ -2915,8 +2887,8 @@ void GdbEngine::loadSymbolsForStack() foreach (const Module &module, modules) { if (module.startAddress <= frame.address && frame.address < module.endAddress) { - runCommand("sharedlibrary " - + dotEscape(module.modulePath.toLocal8Bit())); + runCommand({"sharedlibrary " + dotEscape(module.modulePath.toLocal8Bit()), + NoFlags}); needUpdate = true; } } @@ -3059,7 +3031,7 @@ void GdbEngine::reloadModules() void GdbEngine::reloadModulesInternal() { - runCommand("info shared", CB(handleModulesList), NeedsStop); + runCommand({"info shared", NeedsStop, CB(handleModulesList)}); } static QString nameFromPath(const QString &path) @@ -3246,10 +3218,9 @@ void GdbEngine::loadAdditionalQmlStack() return; } // Call the debug function of QML with the context address to obtain the QML stack trace. - DebuggerCommand cmd = "-data-evaluate-expression \"qt_v4StackTrace((QV4::ExecutionContext *)0x" - + QByteArray::number(contextAddress, 16) + ")\""; - cmd.callback = CB(handleQmlStackTrace); - runCommand(cmd); + runCommand({"-data-evaluate-expression \"qt_v4StackTrace((QV4::ExecutionContext *)0x" + + QByteArray::number(contextAddress, 16) + ")\"", + NoFlags, CB(handleQmlStackTrace)}); }; runCommand(cmd); } @@ -3345,8 +3316,7 @@ void GdbEngine::activateFrame(int frameIndex) // after a response to this -stack-select-frame here. //if (!m_currentThread.isEmpty()) // cmd += " --thread " + m_currentThread; - DebuggerCommand cmd("-stack-select-frame " + QByteArray::number(frameIndex), Discardable); - runCommand(cmd); + runCommand({"-stack-select-frame " + QByteArray::number(frameIndex), Discardable}); } updateLocals(); @@ -3366,14 +3336,14 @@ void GdbEngine::handleThreadInfo(const DebuggerResponse &response) } updateViews(); // Adjust Threads combobox. if (boolSetting(ShowThreadNames)) { - runCommand("threadnames " + action(MaximalStackDepth)->value().toByteArray(), - CB(handleThreadNames), Discardable); + runCommand({"threadnames " + action(MaximalStackDepth)->value().toByteArray(), + Discardable, CB(handleThreadNames)}); } reloadStack(); // Will trigger register reload. } else { // Fall back for older versions: Try to get at least a list // of running threads. - runCommand("-thread-list-ids", CB(handleThreadListIds), Discardable); + runCommand({"-thread-list-ids", Discardable, CB(handleThreadListIds)}); } } @@ -3474,15 +3444,17 @@ void GdbEngine::reloadRegisters() if (!m_registerNamesListed) { // The MI version does not give register size. // runCommand("-data-list-register-names", CB(handleRegisterListNames)); - runCommand("maintenance print raw-registers", CB(handleRegisterListing)); + runCommand({"maintenance print raw-registers", NoFlags, + CB(handleRegisterListing)}); m_registerNamesListed = true; } // Can cause i386-linux-nat.c:571: internal-error: Got request // for bad register number 41.\nA problem internal to GDB has been detected. - runCommand("-data-list-register-values r", - CB(handleRegisterListValues), Discardable); + runCommand({"-data-list-register-values r", Discardable, + CB(handleRegisterListValues)}); } else { - runCommand("maintenance print cooked-registers", CB(handleMaintPrintRegisters)); + runCommand({"maintenance print cooked-registers", NoFlags, + CB(handleMaintPrintRegisters)}); } } @@ -3547,7 +3519,7 @@ void GdbEngine::setRegisterValue(const QByteArray &name, const QString &value) QByteArray fullName = name; if (name.startsWith("xmm")) fullName += ".uint128"; - runCommand("set $" + fullName + "=" + value.toLatin1()); + runCommand({"set $" + fullName + "=" + value.toLatin1(), NoFlags}); reloadRegisters(); } @@ -3684,9 +3656,8 @@ void GdbEngine::watchPoint(const QPoint &pnt) { QByteArray x = QByteArray::number(pnt.x()); QByteArray y = QByteArray::number(pnt.y()); - runCommand("print " + qtNamespace() + "QApplication::widgetAt(" - + x + ',' + y + ')', - CB(handleWatchPoint), NeedsStop); + runCommand({"print " + qtNamespace() + "QApplication::widgetAt(" + x + ',' + y + ')', + NeedsStop, CB(handleWatchPoint)}); } void GdbEngine::handleWatchPoint(const DebuggerResponse &response) @@ -3822,9 +3793,9 @@ public: void GdbEngine::fetchDisassembler(DisassemblerAgent *agent) { if (boolSetting(IntelFlavor)) - runCommand("set disassembly-flavor intel"); + runCommand({"set disassembly-flavor intel"}); else - runCommand("set disassembly-flavor att"); + runCommand({"set disassembly-flavor att"}); fetchDisassemblerByCliPointMixed(agent); } @@ -4025,9 +3996,9 @@ void GdbEngine::startGdb(const QStringList &args) } showMessage(_("GDB STARTED, INITIALIZING IT")); - runCommand("show version", CB(handleShowVersion)); + runCommand({"show version", NoFlags, CB(handleShowVersion)}); //runCommand("-list-features", CB(handleListFeatures)); - runCommand("show debug-file-directory",CB(handleDebugInfoLocation)); + runCommand({"show debug-file-directory", NoFlags, CB(handleDebugInfoLocation)}); //runCommand("-enable-timings"); //rurun print static-members off"); // Seemingly doesn't work. @@ -4037,7 +4008,7 @@ void GdbEngine::startGdb(const QStringList &args) //runCommand("define hookpost-stop\nprint 5\nend"); //runCommand("define hook-call\nprint 6\nend"); //runCommand("define hookpost-call\nprint 7\nend"); - runCommand("set print object on"); + runCommand({"set print object on"}); //runCommand("set step-mode on"); // we can't work with that yes //runCommand("set exec-done-display on"); //runCommand("set print pretty on"); @@ -4048,8 +4019,8 @@ void GdbEngine::startGdb(const QStringList &args) // (Mac OS 10.6), but does so for gdb-966 (10.5): //runCommand("set print inferior-events 1"); - runCommand("set breakpoint pending on"); - runCommand("set print elements 10000"); + runCommand({"set breakpoint pending on"}); + runCommand({"set print elements 10000"}); // Produces a few messages during symtab loading //runCommand("set verbose on"); @@ -4077,11 +4048,11 @@ void GdbEngine::startGdb(const QStringList &args) // We need "print" as otherwise we will get no feedback whatsoever // when Custom DebuggingHelper crash (which happen regularly when accessing // uninitialized variables). - runCommand("handle SIGSEGV nopass stop print"); + runCommand({"handle SIGSEGV nopass stop print"}); - runCommand("set unwindonsignal on"); - runCommand("set width 0"); - runCommand("set height 0"); + runCommand({"set unwindonsignal on"}); + runCommand({"set width 0"}); + runCommand({"set height 0"}); // FIXME: Provide proper Gui settings for these: //runCommand("set breakpoint always-inserted on", ConsoleCommand); @@ -4111,39 +4082,39 @@ void GdbEngine::startGdb(const QStringList &args) for (auto it = completeSourcePathMap.constBegin(), cend = completeSourcePathMap.constEnd(); it != cend; ++it) { - runCommand("set substitute-path " + it.key().toLocal8Bit() - + " " + it.value().toLocal8Bit()); + runCommand({"set substitute-path " + it.key().toLocal8Bit() + + " " + it.value().toLocal8Bit(), NoFlags}); } // Spaces just will not work. foreach (const QString &src, rp.debugSourceLocation) { if (QDir(src).exists()) - runCommand("directory " + src.toLocal8Bit()); + runCommand({"directory " + src.toLocal8Bit(), NoFlags}); else showMessage(_("# directory does not exist: ") + src, LogInput); } const QByteArray sysroot = rp.sysRoot.toLocal8Bit(); if (!sysroot.isEmpty()) { - runCommand("set sysroot " + sysroot); + runCommand({"set sysroot " + sysroot, NoFlags}); // sysroot is not enough to correctly locate the sources, so explicitly // relocate the most likely place for the debug source - runCommand("set substitute-path /usr/src " + sysroot + "/usr/src"); + runCommand({"set substitute-path /usr/src " + sysroot + "/usr/src", NoFlags}); } //QByteArray ba = QFileInfo(sp.dumperLibrary).path().toLocal8Bit(); //if (!ba.isEmpty()) // runCommand("set solib-search-path " + ba); if (attemptQuickStart()) { - runCommand("set auto-solib-add off", ConsoleCommand); + runCommand({"set auto-solib-add off", ConsoleCommand}); } else { m_fullStartDone = true; - runCommand("set auto-solib-add on", ConsoleCommand); + runCommand({"set auto-solib-add on", ConsoleCommand}); } if (boolSetting(MultiInferior)) { //runCommand("set follow-exec-mode new"); - runCommand("set detach-on-fork off"); + runCommand({"set detach-on-fork off"}); } // Finally, set up Python. @@ -4154,15 +4125,15 @@ void GdbEngine::startGdb(const QStringList &args) ICore::resourcePath().toLocal8Bit() + "/debugger/"; if (terminal()->isUsable()) - runCommand("set inferior-tty " + terminal()->slaveDevice()); + runCommand({"set inferior-tty " + terminal()->slaveDevice(), NoFlags}); const QFileInfo gdbBinaryFile(m_gdb); const QByteArray uninstalledData = gdbBinaryFile.absolutePath().toLocal8Bit() + "/data-directory/python"; - runCommand("python sys.path.insert(1, '" + dumperSourcePath + "')"); - runCommand("python sys.path.append('" + uninstalledData + "')"); - runCommand("python from gdbbridge import *"); + runCommand({"python sys.path.insert(1, '" + dumperSourcePath + "')", NoFlags}); + runCommand({"python sys.path.append('" + uninstalledData + "')", NoFlags}); + runCommand({"python from gdbbridge import *", NoFlags}); const QString path = stringSetting(ExtraDumperFile); if (!path.isEmpty() && QFileInfo(path).isReadable()) { @@ -4173,11 +4144,9 @@ void GdbEngine::startGdb(const QStringList &args) const QString commands = stringSetting(ExtraDumperCommands); if (!commands.isEmpty()) - runCommand(commands.toLocal8Bit()); + runCommand({commands.toLocal8Bit(), NoFlags}); - DebuggerCommand cmd("loadDumpers", PythonCommand); - cmd.callback = CB(handlePythonSetup); - runCommand(cmd); + runCommand({"loadDumpers", PythonCommand, CB(handlePythonSetup)}); } void GdbEngine::handleGdbStartFailed() @@ -4189,7 +4158,7 @@ void GdbEngine::loadInitScript() const QString script = runParameters().overrideStartScript; if (!script.isEmpty()) { if (QFileInfo(script).isReadable()) { - runCommand("source " + script.toLocal8Bit()); + runCommand({"source " + script.toLocal8Bit(), NoFlags}); } else { AsynchronousMessageBox::warning( tr("Cannot find debugger initialization script"), @@ -4201,7 +4170,7 @@ void GdbEngine::loadInitScript() } else { const QString commands = stringSetting(GdbStartupCommands); if (!commands.isEmpty()) - runCommand(commands.toLocal8Bit()); + runCommand({commands.toLocal8Bit(), NoFlags}); } } @@ -4211,15 +4180,16 @@ void GdbEngine::setEnvironmentVariables() Environment runEnv = runParameters().environment; foreach (const EnvironmentItem &item, sysEnv.diff(runEnv)) { if (item.unset) - runCommand("unset environment " + item.name.toUtf8()); + runCommand({"unset environment " + item.name.toUtf8(), NoFlags}); else - runCommand("-gdb-set environment " + item.name.toUtf8() + '=' + item.value.toUtf8()); + runCommand({"-gdb-set environment " + item.name.toUtf8() + '=' + + item.value.toUtf8(), NoFlags}); } } void GdbEngine::reloadDebuggingHelpers() { - runCommand("reloadDumpers", PythonCommand); + runCommand({"reloadDumpers", PythonCommand}); reloadLocals(); } @@ -4274,7 +4244,7 @@ void GdbEngine::resetInferior() foreach (QByteArray command, commands.split('\n')) { command = command.trimmed(); if (!command.isEmpty()) - runCommand(command, int(ConsoleCommand | NeedsStop)); + runCommand({command, ConsoleCommand | NeedsStop}); } } m_rerunPending = true; @@ -4313,16 +4283,15 @@ void GdbEngine::handleInferiorPrepared() if (!rp.commandsAfterConnect.isEmpty()) { QByteArray commands = globalMacroExpander()->expand(rp.commandsAfterConnect); - foreach (QByteArray command, commands.split('\n')) { - runCommand(command); - } + foreach (QByteArray command, commands.split('\n')) + runCommand({command, NoFlags}); } //runCommand("set follow-exec-mode new"); if (rp.breakOnMain) { QByteArray cmd = "tbreak "; cmd += rp.toolChainAbi.os() == Abi::WindowsOS ? "qMain" : "main"; - runCommand(cmd); + runCommand({cmd, NoFlags}); } // Initial attempt to set breakpoints. @@ -4344,23 +4313,24 @@ void GdbEngine::finishInferiorSetup() { CHECK_STATE(InferiorSetupRequested); - if (runParameters().startMode == AttachCore) { - notifyInferiorSetupOk(); // No breakpoints in core files. - } else { - if (boolSetting(BreakOnAbort)) - runCommand("-break-insert -f abort"); - if (boolSetting(BreakOnWarning)) { - runCommand("-break-insert -f '" + qtNamespace() + "qWarning'"); - runCommand("-break-insert -f '" + qtNamespace() + "QMessageLogger::warning'"); - } - if (boolSetting(BreakOnFatal)) { - auto cb = [this](const DebuggerResponse &r) { handleBreakOnQFatal(r, false); }; - runCommand("-break-insert -f '" + qtNamespace() + "qFatal'", cb); - runCommand("-break-insert -f '" + qtNamespace() + "QMessageLogger::fatal'", cb); - } else { - notifyInferiorSetupOk(); + if (runParameters().startMode != AttachCore) { // No breakpoints in core files. + const bool onAbort = boolSetting(BreakOnAbort); + const bool onWarning = boolSetting(BreakOnWarning); + const bool onFatal = boolSetting(BreakOnFatal); + if (onAbort || onWarning || onFatal) { + DebuggerCommand cmd("createSpecialBreakpoints", PythonCommand); + cmd.arg("breakonabort", onAbort); + cmd.arg("breakonwarning", onWarning); + cmd.arg("breakonfatal", onFatal); + runCommand(cmd); } } + + // It is ok to cut corners here and not wait for createSpecialBreakpoints()'s + // response, as the command is synchronous from Creator's point of view, + // and even if it fails (e.g. due to stripped binaries), continuing with + // the start up is the best we can do. + notifyInferiorSetupOk(); } void GdbEngine::handleDebugInfoLocation(const DebuggerResponse &response) @@ -4369,34 +4339,14 @@ void GdbEngine::handleDebugInfoLocation(const DebuggerResponse &response) const QByteArray debugInfoLocation = runParameters().debugInfoLocation.toLocal8Bit(); if (QFile::exists(QString::fromLocal8Bit(debugInfoLocation))) { const QByteArray curDebugInfoLocations = response.consoleStreamOutput.split('"').value(1); - if (curDebugInfoLocations.isEmpty()) { - runCommand("set debug-file-directory " + debugInfoLocation); - } else { - runCommand("set debug-file-directory " + debugInfoLocation - + HostOsInfo::pathListSeparator().toLatin1() - + curDebugInfoLocations); - } + QByteArray cmd = "set debug-file-directory " + debugInfoLocation; + if (!curDebugInfoLocations.isEmpty()) + cmd += HostOsInfo::pathListSeparator().toLatin1() + curDebugInfoLocations; + runCommand({cmd, NoFlags}); } } } -void GdbEngine::handleBreakOnQFatal(const DebuggerResponse &response, bool continueSetup) -{ - if (response.resultClass == ResultDone) { - GdbMi bkpt = response.data["bkpt"]; - GdbMi number = bkpt["number"]; - BreakpointResponseId rid(number.data()); - if (rid.isValid()) { - m_qFatalBreakpointResponseId = rid; - runCommand("-break-commands " + number.data() + " return"); - } - } - - // Continue setup. - if (continueSetup) - notifyInferiorSetupOk(); -} - void GdbEngine::notifyInferiorSetupFailed(const QString &msg) { showStatusMessage(tr("Failed to start application:") + QLatin1Char(' ') + msg); @@ -4457,16 +4407,6 @@ void GdbEngine::resetCommandQueue() } } -bool GdbEngine::isQFatalBreakpoint(const BreakpointResponseId &id) const -{ - return id.isValid() && m_qFatalBreakpointResponseId == id; -} - -bool GdbEngine::isHiddenBreakpoint(const BreakpointResponseId &id) const -{ - return isQFatalBreakpoint(id); -} - bool GdbEngine::usesExecInterrupt() const { DebuggerStartMode mode = runParameters().startMode; diff --git a/src/plugins/debugger/gdb/gdbengine.h b/src/plugins/debugger/gdb/gdbengine.h index cee83ca3a98..4c08219ddfa 100644 --- a/src/plugins/debugger/gdb/gdbengine.h +++ b/src/plugins/debugger/gdb/gdbengine.h @@ -172,12 +172,7 @@ private: ////////// Gdb Command Management ////////// }; Q_DECLARE_FLAGS(GdbCommandFlags, GdbCommandFlag) -protected: - void runCommand(const DebuggerCommand &command); - void runCommand(const QByteArray &command, int flags); - void runCommand(const QByteArray &command, - const DebuggerCommand::Callback &callback, - int flags = NoFlags); + void runCommand(const DebuggerCommand &command) override; private: Q_SLOT void commandTimeout(); @@ -336,8 +331,6 @@ private: ////////// View & Data Stuff ////////// void fetchDisassemblerByCliRangePlain(const DisassemblerAgentCookie &ac); bool handleCliDisassemblerResult(const QByteArray &response, DisassemblerAgent *agent); - void handleBreakOnQFatal(const DebuggerResponse &response, bool continueSetup); - // // Source file specific stuff // @@ -426,14 +419,10 @@ protected: // while updating locals. bool m_inUpdateLocals; - bool isQFatalBreakpoint(const BreakpointResponseId &id) const; - bool isHiddenBreakpoint(const BreakpointResponseId &id) const; - // HACK: QByteArray m_currentThread; QString m_lastWinException; QString m_lastMissingDebugInfo; - BreakpointResponseId m_qFatalBreakpointResponseId; bool m_terminalTrap; bool m_temporaryStopPending; bool usesExecInterrupt() const; diff --git a/src/plugins/debugger/gdb/gdbplainengine.cpp b/src/plugins/debugger/gdb/gdbplainengine.cpp index 80e6d18e5b6..37568f726f6 100644 --- a/src/plugins/debugger/gdb/gdbplainengine.cpp +++ b/src/plugins/debugger/gdb/gdbplainengine.cpp @@ -60,10 +60,10 @@ void GdbPlainEngine::setupInferior() setEnvironmentVariables(); if (!runParameters().processArgs.isEmpty()) { QString args = runParameters().processArgs; - runCommand("-exec-arguments " + toLocalEncoding(args)); + runCommand({"-exec-arguments " + toLocalEncoding(args), NoFlags}); } - runCommand("-file-exec-and-symbols \"" + execFilePath() + '"', - CB(handleFileExecAndSymbols)); + runCommand({"-file-exec-and-symbols \"" + execFilePath() + '"', NoFlags, + CB(handleFileExecAndSymbols)}); } void GdbPlainEngine::handleFileExecAndSymbols(const DebuggerResponse &response) @@ -84,9 +84,9 @@ void GdbPlainEngine::handleFileExecAndSymbols(const DebuggerResponse &response) void GdbPlainEngine::runEngine() { if (runParameters().useContinueInsteadOfRun) - runCommand("-exec-continue", CB(handleExecuteContinue), RunRequest); + runCommand({"-exec-continue", RunRequest, CB(handleExecuteContinue)}); else - runCommand("-exec-run", CB(handleExecRun), RunRequest); + runCommand({"-exec-run", RunRequest, CB(handleExecRun)}); } void GdbPlainEngine::handleExecRun(const DebuggerResponse &response) { @@ -98,7 +98,7 @@ void GdbPlainEngine::handleExecRun(const DebuggerResponse &response) showMessage(msgInferiorSetupOk(), StatusBar); // FIXME: That's the wrong place for it. if (boolSetting(EnableReverseDebugging)) - runCommand("target record"); + runCommand({"target record", NoFlags}); } else { QString msg = fromLocalEncoding(response.data["msg"].data()); //QTC_CHECK(status() == InferiorRunOk); diff --git a/src/plugins/debugger/gdb/remotegdbserveradapter.cpp b/src/plugins/debugger/gdb/remotegdbserveradapter.cpp index 75d7f9b633c..98488d1a047 100644 --- a/src/plugins/debugger/gdb/remotegdbserveradapter.cpp +++ b/src/plugins/debugger/gdb/remotegdbserveradapter.cpp @@ -183,10 +183,10 @@ void GdbRemoteServerEngine::setupInferior() // postCommand("set architecture " + remoteArch); const QString solibSearchPath = rp.solibSearchPath.join(HostOsInfo::pathListSeparator()); if (!solibSearchPath.isEmpty()) - runCommand("set solib-search-path " + solibSearchPath.toLocal8Bit()); + runCommand({"set solib-search-path " + solibSearchPath.toLocal8Bit(), NoFlags}); if (!args.isEmpty()) - runCommand("-exec-arguments " + args.toLocal8Bit()); + runCommand({"-exec-arguments " + args.toLocal8Bit(), NoFlags}); setEnvironmentVariables(); @@ -210,7 +210,7 @@ void GdbRemoteServerEngine::setupInferior() // mi_execute_async_cli_command: Assertion `is_running (inferior_ptid)' // failed.\nA problem internal to GDB has been detected,[...] if (boolSetting(TargetAsync)) - runCommand("set target-async on", CB(handleSetTargetAsync)); + runCommand({"set target-async on", NoFlags, CB(handleSetTargetAsync)}); if (executableFileName.isEmpty()) { showMessage(tr("No symbol file given."), StatusBar); @@ -219,8 +219,8 @@ void GdbRemoteServerEngine::setupInferior() } if (!executableFileName.isEmpty()) { - runCommand("-file-exec-and-symbols \"" + executableFileName.toLocal8Bit() + '"', - CB(handleFileExecAndSymbols)); + runCommand({"-file-exec-and-symbols \"" + executableFileName.toLocal8Bit() + '"', + NoFlags, CB(handleFileExecAndSymbols)}); } } @@ -270,11 +270,11 @@ void GdbRemoteServerEngine::callTargetRemote() } if (m_isQnxGdb) - runCommand("target qnx " + channel, CB(handleTargetQnx)); + runCommand({"target qnx " + channel, NoFlags, CB(handleTargetQnx)}); else if (runParameters().multiProcess) - runCommand("target extended-remote " + channel, CB(handleTargetExtendedRemote)); + runCommand({"target extended-remote " + channel, NoFlags, CB(handleTargetExtendedRemote)}); else - runCommand("target remote " + channel, CB(handleTargetRemote)); + runCommand({"target remote " + channel, NoFlags, CB(handleTargetRemote)}); } void GdbRemoteServerEngine::handleTargetRemote(const DebuggerResponse &response) @@ -287,7 +287,7 @@ void GdbRemoteServerEngine::handleTargetRemote(const DebuggerResponse &response) QString postAttachCommands = stringSetting(GdbPostAttachCommands); if (!postAttachCommands.isEmpty()) { foreach (const QString &cmd, postAttachCommands.split(QLatin1Char('\n'))) - runCommand(cmd.toLatin1()); + runCommand({cmd.toLatin1(), NoFlags}); } handleInferiorPrepared(); } else { @@ -307,15 +307,15 @@ void GdbRemoteServerEngine::handleTargetExtendedRemote(const DebuggerResponse &r QString postAttachCommands = stringSetting(GdbPostAttachCommands); if (!postAttachCommands.isEmpty()) { foreach (const QString &cmd, postAttachCommands.split(QLatin1Char('\n'))) - runCommand(cmd.toLatin1()); + runCommand({cmd.toLatin1(), NoFlags}); } if (runParameters().attachPID > 0) { // attach to pid if valid // gdb server will stop the remote application itself. - runCommand("attach " + QByteArray::number(runParameters().attachPID), - CB(handleTargetExtendedAttach)); + runCommand({"attach " + QByteArray::number(runParameters().attachPID), + NoFlags, CB(handleTargetExtendedAttach)}); } else { - runCommand("-gdb-set remote exec-file " + runParameters().remoteExecutable.toLatin1(), - CB(handleTargetExtendedAttach)); + runCommand({"-gdb-set remote exec-file " + runParameters().remoteExecutable.toLatin1(), + NoFlags, CB(handleTargetExtendedAttach)}); } } else { QString msg = msgConnectRemoteServerFailed( @@ -349,9 +349,9 @@ void GdbRemoteServerEngine::handleTargetQnx(const DebuggerResponse &response) const qint64 pid = isMasterEngine() ? runParameters().attachPID : masterEngine()->runParameters().attachPID; const QString remoteExecutable = isMasterEngine() ? runParameters().remoteExecutable : masterEngine()->runParameters().remoteExecutable; if (pid > -1) - runCommand("attach " + QByteArray::number(pid), CB(handleAttach)); + runCommand({"attach " + QByteArray::number(pid), NoFlags, CB(handleAttach)}); else if (!remoteExecutable.isEmpty()) - runCommand("set nto-executable " + remoteExecutable.toLatin1(), CB(handleSetNtoExecutable)); + runCommand({"set nto-executable " + remoteExecutable.toLatin1(), NoFlags, CB(handleSetNtoExecutable)}); else handleInferiorPrepared(); } else { @@ -409,7 +409,7 @@ void GdbRemoteServerEngine::runEngine() const QString remoteExecutable = runParameters().remoteExecutable; if (!remoteExecutable.isEmpty()) { - runCommand("-exec-run", CB(handleExecRun), RunRequest); + runCommand({"-exec-run", RunRequest, CB(handleExecRun)}); } else { notifyEngineRunAndInferiorStopOk(); continueInferiorInternal(); @@ -434,7 +434,7 @@ void GdbRemoteServerEngine::interruptInferior2() { QTC_ASSERT(state() == InferiorStopRequested, qDebug() << state()); if (boolSetting(TargetAsync)) { - runCommand("-exec-interrupt", CB(handleInterruptInferior)); + runCommand({"-exec-interrupt", NoFlags, CB(handleInterruptInferior)}); } else if (m_isQnxGdb && HostOsInfo::isWindowsHost()) { m_gdbProc.interrupt(); } else { diff --git a/src/plugins/debugger/gdb/termgdbadapter.cpp b/src/plugins/debugger/gdb/termgdbadapter.cpp index a53b6b9729c..f50388ee0e7 100644 --- a/src/plugins/debugger/gdb/termgdbadapter.cpp +++ b/src/plugins/debugger/gdb/termgdbadapter.cpp @@ -134,9 +134,8 @@ void GdbTermEngine::runEngine() { QTC_ASSERT(state() == EngineRunRequested, qDebug() << state()); const qint64 attachedPID = m_stubProc.applicationPID(); - DebuggerCommand cmd("attach " + QByteArray::number(attachedPID)); - cmd.callback = [this](const DebuggerResponse &r) { handleStubAttached(r); }; - runCommand(cmd); + runCommand({"attach " + QByteArray::number(attachedPID), NoFlags, + [this](const DebuggerResponse &r) { handleStubAttached(r); }}); } void GdbTermEngine::handleStubAttached(const DebuggerResponse &response) diff --git a/src/plugins/debugger/lldb/lldbengine.cpp b/src/plugins/debugger/lldb/lldbengine.cpp index bb5fa23620e..a372709977d 100644 --- a/src/plugins/debugger/lldb/lldbengine.cpp +++ b/src/plugins/debugger/lldb/lldbengine.cpp @@ -130,17 +130,17 @@ void LldbEngine::executeDebuggerCommand(const QString &command, DebuggerLanguage runCommand(cmd); } -void LldbEngine::runCommand(const DebuggerCommand &command_) +void LldbEngine::runCommand(const DebuggerCommand &cmd) { QTC_ASSERT(m_lldbProc.state() == QProcess::Running, notifyEngineIll()); const int tok = ++currentToken(); - DebuggerCommand command = command_; + DebuggerCommand command = cmd; command.arg("token", tok); QByteArray token = QByteArray::number(tok); - QByteArray cmd = command.function + "(" + command.argsToPython() + ")"; - showMessage(_(token + cmd + '\n'), LogInput); + QByteArray function = command.function + "(" + command.argsToPython() + ")"; + showMessage(_(token + function + '\n'), LogInput); m_commandForToken[currentToken()] = command; - m_lldbProc.write("script theDumper." + cmd + "\n"); + m_lldbProc.write("script theDumper." + function + "\n"); } void LldbEngine::debugLastCommand() @@ -151,7 +151,7 @@ void LldbEngine::debugLastCommand() void LldbEngine::shutdownInferior() { QTC_ASSERT(state() == InferiorShutdownRequested, qDebug() << state()); - runCommand(DebuggerCommand("shutdownInferior")); + runCommand({"shutdownInferior"}); } void LldbEngine::shutdownEngine() @@ -405,37 +405,37 @@ void LldbEngine::runEngine() void LldbEngine::interruptInferior() { showStatusMessage(tr("Interrupt requested..."), 5000); - runCommand("interruptInferior"); + runCommand({"interruptInferior"}); } void LldbEngine::executeStep() { notifyInferiorRunRequested(); - runCommand("executeStep"); + runCommand({"executeStep"}); } void LldbEngine::executeStepI() { notifyInferiorRunRequested(); - runCommand("executeStepI"); + runCommand({"executeStepI"}); } void LldbEngine::executeStepOut() { notifyInferiorRunRequested(); - runCommand("executeStepOut"); + runCommand({"executeStepOut"}); } void LldbEngine::executeNext() { notifyInferiorRunRequested(); - runCommand("executeNext"); + runCommand({"executeNext"}); } void LldbEngine::executeNextI() { notifyInferiorRunRequested(); - runCommand("executeNextI"); + runCommand({"executeNextI"}); } void LldbEngine::continueInferior() @@ -984,7 +984,7 @@ void LldbEngine::reloadRegisters() void LldbEngine::reloadDebuggingHelpers() { - runCommand("reloadDumpers"); + runCommand({"reloadDumpers"}); updateAll(); } @@ -1037,7 +1037,7 @@ void LldbEngine::fetchFullBacktrace() Internal::openTextEditor(_("Backtrace $"), QString::fromUtf8(QByteArray::fromHex(response.data.data()))); }; - runCommand("fetchFullBacktrace"); + runCommand(cmd); } void LldbEngine::fetchMemory(MemoryAgent *agent, QObject *editorToken, diff --git a/src/plugins/debugger/lldb/lldbengine.h b/src/plugins/debugger/lldb/lldbengine.h index b655794a285..60b604eb9e6 100644 --- a/src/plugins/debugger/lldb/lldbengine.h +++ b/src/plugins/debugger/lldb/lldbengine.h @@ -147,7 +147,7 @@ private: void notifyEngineRemoteSetupFinished(const RemoteSetupResult &result) override; - void runCommand(const DebuggerCommand &cmd); + void runCommand(const DebuggerCommand &cmd) override; void debugLastCommand() override; private: diff --git a/src/plugins/debugger/pdb/pdbengine.cpp b/src/plugins/debugger/pdb/pdbengine.cpp index 7fa38015676..57b242dc4bc 100644 --- a/src/plugins/debugger/pdb/pdbengine.cpp +++ b/src/plugins/debugger/pdb/pdbengine.cpp @@ -300,7 +300,7 @@ void PdbEngine::loadAllSymbols() void PdbEngine::reloadModules() { - runCommand("listModules"); + runCommand({"listModules"}); } void PdbEngine::refreshModules(const GdbMi &modules) @@ -557,7 +557,7 @@ void PdbEngine::refreshStack(const GdbMi &stack) void PdbEngine::updateAll() { - runCommand("stackListFrames"); + runCommand({"stackListFrames"}); updateLocals(); } diff --git a/src/plugins/debugger/pdb/pdbengine.h b/src/plugins/debugger/pdb/pdbengine.h index 7fa34bb1d87..ca3f595fc03 100644 --- a/src/plugins/debugger/pdb/pdbengine.h +++ b/src/plugins/debugger/pdb/pdbengine.h @@ -98,7 +98,7 @@ private: bool isSynchronous() const override { return true; } void updateItem(const QByteArray &iname) override; - void runCommand(const DebuggerCommand &cmd); + void runCommand(const DebuggerCommand &cmd) override; void postDirectCommand(const QByteArray &command); void refreshLocation(const GdbMi &reportedLocation); diff --git a/src/plugins/debugger/qml/qmlengine.cpp b/src/plugins/debugger/qml/qmlengine.cpp index 5031575d29b..c4b6fcf7c77 100644 --- a/src/plugins/debugger/qml/qmlengine.cpp +++ b/src/plugins/debugger/qml/qmlengine.cpp @@ -612,7 +612,7 @@ void QmlEngine::shutdownInferior() // "type" : "request", // "command" : "disconnect", // } - d->runCommand(DISCONNECT); + d->runCommand({DISCONNECT}); if (isSlaveEngine()) resetLocation(); @@ -2504,7 +2504,7 @@ void QmlEnginePrivate::stateChanged(State state) /// Start session. flushSendBuffer(); runDirectCommand(CONNECT); - runCommand(VERSION); // Only used for logging. + runCommand({VERSION}); // Only used for logging. } } diff --git a/src/plugins/debugger/simplifytype.cpp b/src/plugins/debugger/simplifytype.cpp index c4012b173cb..48d9656fe65 100644 --- a/src/plugins/debugger/simplifytype.cpp +++ b/src/plugins/debugger/simplifytype.cpp @@ -123,6 +123,7 @@ QString simplifyType(const QString &typeIn) type.remove(0, 7); const bool isLibCpp = type.contains(QLatin1String("std::__1")); + type.replace(QLatin1String("std::__cxx11::"), QLatin1String("std::")); type.replace(QLatin1String("std::__1::"), QLatin1String("std::")); type.replace(QLatin1String("std::__debug::"), QLatin1String("std::")); QRegExp simpleStringRE(QString::fromLatin1("std::basic_string ?")); diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp index c67efb3d9eb..819f152dbe3 100644 --- a/src/plugins/git/gitclient.cpp +++ b/src/plugins/git/gitclient.cpp @@ -1282,7 +1282,6 @@ bool GitClient::synchronousRevListCmd(const QString &workingDirectory, const QSt // Find out the immediate parent revisions of a revision of the repository. // Might be several in case of merges. bool GitClient::synchronousParentRevisions(const QString &workingDirectory, - const QStringList &files /* = QStringList() */, const QString &revision, QStringList *parents, QString *errorMessage) const @@ -1295,10 +1294,6 @@ bool GitClient::synchronousParentRevisions(const QString &workingDirectory, return true; } arguments << QLatin1String("--parents") << QLatin1String("--max-count=1") << revision; - if (!files.isEmpty()) { - arguments.append(QLatin1String("--")); - arguments.append(files); - } if (!synchronousRevListCmd(workingDirectory, arguments, &outputText, &errorText)) { *errorMessage = msgParentRevisionFailed(workingDirectory, revision, errorText); @@ -1474,7 +1469,7 @@ void GitClient::synchronousTagsForCommit(const QString &workingDirectory, const QStringList parents; QString errorMessage; - synchronousParentRevisions(workingDirectory, QStringList(), revision, &parents, &errorMessage); + synchronousParentRevisions(workingDirectory, revision, &parents, &errorMessage); foreach (const QString &p, parents) { QByteArray pf; arguments.clear(); @@ -1534,7 +1529,8 @@ QString GitClient::synchronousShortDescription(const QString &workingDirectory, arguments << QLatin1String("log") << QLatin1String(noColorOption) << (QLatin1String("--pretty=format:") + format) << QLatin1String("--max-count=1") << revision; - const bool rc = vcsFullySynchronousExec(workingDirectory, arguments, &outputTextData, &errorText); + const bool rc = vcsFullySynchronousExec(workingDirectory, arguments, &outputTextData, &errorText, + silentFlags); if (!rc) { VcsOutputWindow::appendSilently(tr("Cannot describe revision \"%1\" in \"%2\": %3") .arg(revision, workingDirectory, commandOutputFromLocal8Bit(errorText))); diff --git a/src/plugins/git/gitclient.h b/src/plugins/git/gitclient.h index d226c686d54..51825a768f0 100644 --- a/src/plugins/git/gitclient.h +++ b/src/plugins/git/gitclient.h @@ -237,7 +237,6 @@ public: QString *output, QString *errorMessage = 0) const; bool synchronousParentRevisions(const QString &workingDirectory, - const QStringList &files /* = QStringList() */, const QString &revision, QStringList *parents, QString *errorMessage) const; diff --git a/src/plugins/git/giteditor.cpp b/src/plugins/git/giteditor.cpp index 944a9ba676d..45fee1db5dd 100644 --- a/src/plugins/git/giteditor.cpp +++ b/src/plugins/git/giteditor.cpp @@ -309,8 +309,7 @@ QStringList GitEditorWidget::annotationPreviousVersions(const QString &revision) const QFileInfo fi(source()); const QString workingDirectory = fi.absolutePath(); // Get the SHA1's of the file. - if (!client->synchronousParentRevisions(workingDirectory, QStringList(fi.fileName()), - revision, &revisions, &errorMessage)) { + if (!client->synchronousParentRevisions(workingDirectory, revision, &revisions, &errorMessage)) { VcsOutputWindow::appendSilently(errorMessage); return QStringList(); } diff --git a/src/plugins/help/macwebkithelpviewer.mm b/src/plugins/help/macwebkithelpviewer.mm index b5476d32f73..05cf062e9c6 100644 --- a/src/plugins/help/macwebkithelpviewer.mm +++ b/src/plugins/help/macwebkithelpviewer.mm @@ -34,7 +34,6 @@ #include "openpagesmanager.h" #include -#include #include #include @@ -437,18 +436,19 @@ MacWebKitHelpWidget::MacWebKitHelpWidget(MacWebKitHelpViewer *parent) { d->m_toolTipTimer.setSingleShot(true); connect(&d->m_toolTipTimer, &QTimer::timeout, this, &MacWebKitHelpWidget::showToolTip); - AutoreleasePool pool; Q_UNUSED(pool) - d->m_webView = [[MyWebView alloc] init]; - // Turn layered rendering on. - // Otherwise the WebView will render empty after any QQuickWidget was shown. - d->m_webView.wantsLayer = YES; - d->m_frameLoadDelegate = [[FrameLoadDelegate alloc] initWithMainFrame:d->m_webView.mainFrame - viewer:parent]; - [d->m_webView setFrameLoadDelegate:d->m_frameLoadDelegate]; - d->m_uiDelegate = [[UIDelegate alloc] initWithWidget:this]; - [d->m_webView setUIDelegate:d->m_uiDelegate]; + @autoreleasepool { + d->m_webView = [[MyWebView alloc] init]; + // Turn layered rendering on. + // Otherwise the WebView will render empty after any QQuickWidget was shown. + d->m_webView.wantsLayer = YES; + d->m_frameLoadDelegate = [[FrameLoadDelegate alloc] initWithMainFrame:d->m_webView.mainFrame + viewer:parent]; + [d->m_webView setFrameLoadDelegate:d->m_frameLoadDelegate]; + d->m_uiDelegate = [[UIDelegate alloc] initWithWidget:this]; + [d->m_webView setUIDelegate:d->m_uiDelegate]; - setCocoaView(d->m_webView); + setCocoaView(d->m_webView); + } } MacWebKitHelpWidget::~MacWebKitHelpWidget() @@ -507,11 +507,12 @@ MacWebKitHelpViewer::MacWebKitHelpViewer(QWidget *parent) new MacResponderHack(qApp); } - AutoreleasePool pool; Q_UNUSED(pool) - QVBoxLayout *layout = new QVBoxLayout; - setLayout(layout); - layout->setContentsMargins(0, 0, 0, 0); - layout->addWidget(m_widget, 10); + @autoreleasepool { + QVBoxLayout *layout = new QVBoxLayout; + setLayout(layout); + layout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(m_widget, 10); + } } MacWebKitHelpViewer::~MacWebKitHelpViewer() @@ -521,43 +522,49 @@ MacWebKitHelpViewer::~MacWebKitHelpViewer() QFont MacWebKitHelpViewer::viewerFont() const { - AutoreleasePool pool; Q_UNUSED(pool) - WebPreferences *preferences = m_widget->webView().preferences; - QString family = QString::fromNSString([preferences standardFontFamily]); - int size = [preferences defaultFontSize]; - return QFont(family, size); + @autoreleasepool { + WebPreferences *preferences = m_widget->webView().preferences; + QString family = QString::fromNSString([preferences standardFontFamily]); + int size = [preferences defaultFontSize]; + return QFont(family, size); + } } void MacWebKitHelpViewer::setViewerFont(const QFont &font) { - AutoreleasePool pool; Q_UNUSED(pool) - WebPreferences *preferences = m_widget->webView().preferences; - [preferences setStandardFontFamily:font.family().toNSString()]; - [preferences setDefaultFontSize:font.pointSize()]; + @autoreleasepool { + WebPreferences *preferences = m_widget->webView().preferences; + [preferences setStandardFontFamily:font.family().toNSString()]; + [preferences setDefaultFontSize:font.pointSize()]; + } } void MacWebKitHelpViewer::scaleUp() { - AutoreleasePool pool; Q_UNUSED(pool) - m_widget->webView().textSizeMultiplier += 0.1; + @autoreleasepool { + m_widget->webView().textSizeMultiplier += 0.1; + } } void MacWebKitHelpViewer::scaleDown() { - AutoreleasePool pool; Q_UNUSED(pool) - m_widget->webView().textSizeMultiplier = qMax(0.1, m_widget->webView().textSizeMultiplier - 0.1); + @autoreleasepool { + m_widget->webView().textSizeMultiplier = qMax(0.1, m_widget->webView().textSizeMultiplier - 0.1); + } } void MacWebKitHelpViewer::resetScale() { - AutoreleasePool pool; Q_UNUSED(pool) - m_widget->webView().textSizeMultiplier = 1.0; + @autoreleasepool { + m_widget->webView().textSizeMultiplier = 1.0; + } } qreal MacWebKitHelpViewer::scale() const { - AutoreleasePool pool; Q_UNUSED(pool) - return m_widget->webView().textSizeMultiplier; + @autoreleasepool { + return m_widget->webView().textSizeMultiplier; + } } void MacWebKitHelpViewer::setScale(qreal scale) @@ -567,24 +574,27 @@ void MacWebKitHelpViewer::setScale(qreal scale) QString MacWebKitHelpViewer::title() const { - AutoreleasePool pool; Q_UNUSED(pool) - return QString::fromNSString(m_widget->webView().mainFrameTitle); + @autoreleasepool { + return QString::fromNSString(m_widget->webView().mainFrameTitle); + } } QUrl MacWebKitHelpViewer::source() const { - AutoreleasePool pool; Q_UNUSED(pool) - WebDataSource *dataSource = m_widget->webView().mainFrame.dataSource; - if (!dataSource) - dataSource = m_widget->webView().mainFrame.provisionalDataSource; - return QUrl::fromNSURL(dataSource.request.URL); + @autoreleasepool { + WebDataSource *dataSource = m_widget->webView().mainFrame.dataSource; + if (!dataSource) + dataSource = m_widget->webView().mainFrame.provisionalDataSource; + return QUrl::fromNSURL(dataSource.request.URL); + } } void MacWebKitHelpViewer::setSource(const QUrl &url) { - AutoreleasePool pool; Q_UNUSED(pool) - ensureProtocolHandler(); - [m_widget->webView().mainFrame loadRequest:[NSURLRequest requestWithURL:url.toNSURL()]]; + @autoreleasepool { + ensureProtocolHandler(); + [m_widget->webView().mainFrame loadRequest:[NSURLRequest requestWithURL:url.toNSURL()]]; + } } void MacWebKitHelpViewer::scrollToAnchor(const QString &anchor) @@ -596,57 +606,63 @@ void MacWebKitHelpViewer::scrollToAnchor(const QString &anchor) void MacWebKitHelpViewer::setHtml(const QString &html) { - AutoreleasePool pool; Q_UNUSED(pool) - [m_widget->webView().mainFrame - loadHTMLString:html.toNSString() - baseURL:[NSURL fileURLWithPath:Core::ICore::resourcePath().toNSString()]]; + @autoreleasepool { + [m_widget->webView().mainFrame + loadHTMLString:html.toNSString() + baseURL:[NSURL fileURLWithPath:Core::ICore::resourcePath().toNSString()]]; + } } QString MacWebKitHelpViewer::selectedText() const { - AutoreleasePool pool; Q_UNUSED(pool) - return QString::fromNSString(m_widget->webView().selectedDOMRange.text); + @autoreleasepool { + return QString::fromNSString(m_widget->webView().selectedDOMRange.text); + } } bool MacWebKitHelpViewer::isForwardAvailable() const { - AutoreleasePool pool; Q_UNUSED(pool) - return m_widget->webView().canGoForward; + @autoreleasepool { + return m_widget->webView().canGoForward; + } } bool MacWebKitHelpViewer::isBackwardAvailable() const { - AutoreleasePool pool; Q_UNUSED(pool) - return m_widget->webView().canGoBack; + @autoreleasepool { + return m_widget->webView().canGoBack; + } } void MacWebKitHelpViewer::addBackHistoryItems(QMenu *backMenu) { - AutoreleasePool pool; Q_UNUSED(pool) - WebBackForwardList *list = m_widget->webView().backForwardList; - int backListCount = list.backListCount; - for (int i = 0; i < backListCount; ++i) { - int historyIndex = -(i+1); - QAction *action = new QAction(backMenu); - action->setText(QString::fromNSString([list itemAtIndex:historyIndex].title)); - action->setData(historyIndex); - connect(action, SIGNAL(triggered()), this, SLOT(goToHistoryItem())); - backMenu->addAction(action); + @autoreleasepool { + WebBackForwardList *list = m_widget->webView().backForwardList; + int backListCount = list.backListCount; + for (int i = 0; i < backListCount; ++i) { + int historyIndex = -(i+1); + QAction *action = new QAction(backMenu); + action->setText(QString::fromNSString([list itemAtIndex:historyIndex].title)); + action->setData(historyIndex); + connect(action, SIGNAL(triggered()), this, SLOT(goToHistoryItem())); + backMenu->addAction(action); + } } } void MacWebKitHelpViewer::addForwardHistoryItems(QMenu *forwardMenu) { - AutoreleasePool pool; Q_UNUSED(pool) - WebBackForwardList *list = m_widget->webView().backForwardList; - int forwardListCount = list.forwardListCount; - for (int i = 0; i < forwardListCount; ++i) { - int historyIndex = i + 1; - QAction *action = new QAction(forwardMenu); - action->setText(QString::fromNSString([list itemAtIndex:historyIndex].title)); - action->setData(historyIndex); - connect(action, SIGNAL(triggered()), this, SLOT(goToHistoryItem())); - forwardMenu->addAction(action); + @autoreleasepool { + WebBackForwardList *list = m_widget->webView().backForwardList; + int forwardListCount = list.forwardListCount; + for (int i = 0; i < forwardListCount; ++i) { + int historyIndex = i + 1; + QAction *action = new QAction(forwardMenu); + action->setText(QString::fromNSString([list itemAtIndex:historyIndex].title)); + action->setData(historyIndex); + connect(action, SIGNAL(triggered()), this, SLOT(goToHistoryItem())); + forwardMenu->addAction(action); + } } } @@ -728,55 +744,56 @@ bool MacWebKitHelpViewer::findText(const QString &text, Core::FindFlags flags, b { Q_UNUSED(incremental); Q_UNUSED(fromSearch); - AutoreleasePool pool; Q_UNUSED(pool) - if (wrapped) - *wrapped = false; - bool forward = !(flags & Core::FindBackward); - bool caseSensitive = (flags & Core::FindCaseSensitively); - WebView *webView = m_widget->webView(); + @autoreleasepool { + if (wrapped) + *wrapped = false; + bool forward = !(flags & Core::FindBackward); + bool caseSensitive = (flags & Core::FindCaseSensitively); + WebView *webView = m_widget->webView(); - // WebView searchFor:.... grabs first responder, and when losing first responder afterwards, - // it removes the selection and forgets the search state, making it pretty useless for us + // WebView searchFor:.... grabs first responder, and when losing first responder afterwards, + // it removes the selection and forgets the search state, making it pretty useless for us - // define the start node and offset for the search - DOMNode *start = nil; // default is search whole body - int startOffset = -1; - DOMRange *selection = webView.selectedDOMRange; - if (selection) { - if (QString::fromNSString(selection.text).compare( - text, caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive) != 0) { - // for incremental search we want to search from the beginning of the selection - start = selection.startContainer; - startOffset = selection.startOffset; - } else { - // search for next occurrence - if (forward) { - start = selection.endContainer; - startOffset = selection.endOffset; - } else { + // define the start node and offset for the search + DOMNode *start = nil; // default is search whole body + int startOffset = -1; + DOMRange *selection = webView.selectedDOMRange; + if (selection) { + if (QString::fromNSString(selection.text).compare( + text, caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive) != 0) { + // for incremental search we want to search from the beginning of the selection start = selection.startContainer; startOffset = selection.startOffset; + } else { + // search for next occurrence + if (forward) { + start = selection.endContainer; + startOffset = selection.endOffset; + } else { + start = selection.startContainer; + startOffset = selection.startOffset; + } } } - } - DOMRange *newSelection = findText(text.toNSString(), forward, caseSensitive, - start, startOffset); - if (!newSelection && start != nil) { // wrap - start = nil; - startOffset = -1; - newSelection = findText(text.toNSString(), forward, caseSensitive, - start, startOffset); - if (newSelection && wrapped) - *wrapped = true; - } - if (newSelection) { - // found, select and scroll there - [webView setSelectedDOMRange:newSelection affinity:NSSelectionAffinityDownstream]; - if (forward) - [newSelection.endContainer.parentElement scrollIntoViewIfNeeded:YES]; - else - [newSelection.startContainer.parentElement scrollIntoViewIfNeeded:YES]; - return true; + DOMRange *newSelection = findText(text.toNSString(), forward, caseSensitive, + start, startOffset); + if (!newSelection && start != nil) { // wrap + start = nil; + startOffset = -1; + newSelection = findText(text.toNSString(), forward, caseSensitive, + start, startOffset); + if (newSelection && wrapped) + *wrapped = true; + } + if (newSelection) { + // found, select and scroll there + [webView setSelectedDOMRange:newSelection affinity:NSSelectionAffinityDownstream]; + if (forward) + [newSelection.endContainer.parentElement scrollIntoViewIfNeeded:YES]; + else + [newSelection.startContainer.parentElement scrollIntoViewIfNeeded:YES]; + return true; + } } return false; } @@ -793,18 +810,20 @@ void MacWebKitHelpViewer::stop() void MacWebKitHelpViewer::forward() { - AutoreleasePool pool; Q_UNUSED(pool) - [m_widget->webView() goForward]; - emit forwardAvailable(isForwardAvailable()); - emit backwardAvailable(isBackwardAvailable()); + @autoreleasepool { + [m_widget->webView() goForward]; + emit forwardAvailable(isForwardAvailable()); + emit backwardAvailable(isBackwardAvailable()); + } } void MacWebKitHelpViewer::backward() { - AutoreleasePool pool; Q_UNUSED(pool) - [m_widget->webView() goBack]; - emit forwardAvailable(isForwardAvailable()); - emit backwardAvailable(isBackwardAvailable()); + @autoreleasepool { + [m_widget->webView() goBack]; + emit forwardAvailable(isForwardAvailable()); + emit backwardAvailable(isBackwardAvailable()); + } } void MacWebKitHelpViewer::print(QPrinter *printer) @@ -826,18 +845,19 @@ void MacWebKitHelpViewer::slotLoadFinished() void MacWebKitHelpViewer::goToHistoryItem() { - AutoreleasePool pool; Q_UNUSED(pool) - QAction *action = qobject_cast(sender()); - QTC_ASSERT(action, return); - bool ok = false; - int index = action->data().toInt(&ok); - QTC_ASSERT(ok, return); - WebBackForwardList *list = m_widget->webView().backForwardList; - WebHistoryItem *item = [list itemAtIndex:index]; - QTC_ASSERT(item, return); - [m_widget->webView() goToBackForwardItem:item]; - emit forwardAvailable(isForwardAvailable()); - emit backwardAvailable(isBackwardAvailable()); + @autoreleasepool { + QAction *action = qobject_cast(sender()); + QTC_ASSERT(action, return); + bool ok = false; + int index = action->data().toInt(&ok); + QTC_ASSERT(ok, return); + WebBackForwardList *list = m_widget->webView().backForwardList; + WebHistoryItem *item = [list itemAtIndex:index]; + QTC_ASSERT(item, return); + [m_widget->webView() goToBackForwardItem:item]; + emit forwardAvailable(isForwardAvailable()); + emit backwardAvailable(isBackwardAvailable()); + } } // #pragma mark -- MacResponderHack @@ -858,13 +878,14 @@ void MacResponderHack::responderHack(QWidget *old, QWidget *now) Q_UNUSED(old) if (!now) return; - AutoreleasePool pool; Q_UNUSED(pool) - NSView *view; - if (QMacCocoaViewContainer *viewContainer = qobject_cast(now)) - view = viewContainer->cocoaView(); - else - view = (NSView *)now->effectiveWinId(); - [view.window makeFirstResponder:view]; + @autoreleasepool { + NSView *view; + if (QMacCocoaViewContainer *viewContainer = qobject_cast(now)) + view = viewContainer->cocoaView(); + else + view = (NSView *)now->effectiveWinId(); + [view.window makeFirstResponder:view]; + } } } // Internal diff --git a/src/plugins/projectexplorer/dependenciespanel.cpp b/src/plugins/projectexplorer/dependenciespanel.cpp index 019d614d44a..f569bc76e29 100644 --- a/src/plugins/projectexplorer/dependenciespanel.cpp +++ b/src/plugins/projectexplorer/dependenciespanel.cpp @@ -233,7 +233,8 @@ DependenciesWidget::DependenciesWidget(Project *project, QWidget *parent) layout->addItem(new QSpacerItem(0, 0 , QSizePolicy::Expanding, QSizePolicy::Fixed), 0, 1); m_cascadeSetActiveCheckBox = new QCheckBox; - m_cascadeSetActiveCheckBox->setText(tr("Synchronize active kit, build, and deploy configuration between projects.")); + m_cascadeSetActiveCheckBox->setText(tr("Synchronize configuration")); + m_cascadeSetActiveCheckBox->setToolTip(tr("Synchronize active kit, build, and deploy configuration between projects.")); m_cascadeSetActiveCheckBox->setChecked(SessionManager::isProjectConfigurationCascading()); connect(m_cascadeSetActiveCheckBox, &QCheckBox::toggled, SessionManager::instance(), &SessionManager::setProjectConfigurationCascading); diff --git a/src/plugins/projectexplorer/deploymentdataview.ui b/src/plugins/projectexplorer/deploymentdataview.ui index ea1bca09940..fce1e99706e 100644 --- a/src/plugins/projectexplorer/deploymentdataview.ui +++ b/src/plugins/projectexplorer/deploymentdataview.ui @@ -14,6 +14,18 @@ Form + + 0 + + + 0 + + + 0 + + + 0 + @@ -22,7 +34,14 @@ - + + + + 100 + 100 + + + diff --git a/src/plugins/projectexplorer/environmentwidget.cpp b/src/plugins/projectexplorer/environmentwidget.cpp index 2ea3f3d0bb6..af0829792cb 100644 --- a/src/plugins/projectexplorer/environmentwidget.cpp +++ b/src/plugins/projectexplorer/environmentwidget.cpp @@ -52,6 +52,7 @@ namespace ProjectExplorer { class EnvironmentValidator : public QValidator { + Q_OBJECT public: EnvironmentValidator(QWidget *parent, Utils::EnvironmentModel *model, QTreeView *view, @@ -409,3 +410,5 @@ void Internal::EnvironmentTreeView::keyPressEvent(QKeyEvent *event) } // namespace ProjectExplorer + +#include "environmentwidget.moc" diff --git a/src/plugins/projectexplorer/kitmanager.cpp b/src/plugins/projectexplorer/kitmanager.cpp index 0125b0e8a94..05f75c3df5e 100644 --- a/src/plugins/projectexplorer/kitmanager.cpp +++ b/src/plugins/projectexplorer/kitmanager.cpp @@ -171,6 +171,8 @@ void KitManager::restoreKits() Kit *toStore = 0; foreach (Kit *current, kitsToValidate) { toStore = current; + toStore->setup(); // Make sure all kitinformation are properly set up before merging them + // with the information from the user settings file // Check whether we had this kit stored and prefer the stored one: for (int i = 0; i < kitsToCheck.count(); ++i) { @@ -191,8 +193,6 @@ void KitManager::restoreKits() break; } } - if (toStore == current) - toStore->setup(); addKit(toStore); sdkKits << toStore; } diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index 69cad4e8242..c4db9a25e6e 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -1161,10 +1161,8 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er dd->m_projectExplorerSettings.environmentId = QUuid::createUuid(); int tmp = s->value(QLatin1String("ProjectExplorer/Settings/StopBeforeBuild"), Utils::HostOsInfo::isWindowsHost() ? 1 : 0).toInt(); - dd->m_projectExplorerSettings.stopBeforeBuild = ProjectExplorerSettings::StopBeforeBuild(tmp); if (tmp < 0 || tmp > ProjectExplorerSettings::StopAll) tmp = Utils::HostOsInfo::isWindowsHost() ? 1 : 0; - dd->m_projectExplorerSettings.stopBeforeBuild = ProjectExplorerSettings::StopBeforeBuild(tmp); connect(dd->m_sessionManagerAction, &QAction::triggered, @@ -2294,14 +2292,13 @@ int ProjectExplorerPluginPrivate::queue(QList projects, QList ste if (!m_instance->saveModifiedFiles()) return -1; - if (m_projectExplorerSettings.stopBeforeBuild != ProjectExplorerSettings::StopNone) { - QList toStop; - foreach (RunControl *rc, m_outputPane->allRunControls()) { - if (rc->isRunning() - && (m_projectExplorerSettings.stopBeforeBuild == ProjectExplorerSettings::StopAll - || projects.contains(rc->project()))) - toStop << rc; - } + if (m_projectExplorerSettings.stopBeforeBuild != ProjectExplorerSettings::StopNone + && stepIds.contains(Constants::BUILDSTEPS_BUILD)) { + bool stopAll = (m_projectExplorerSettings.stopBeforeBuild == ProjectExplorerSettings::StopAll); + const QList toStop + = Utils::filtered(m_outputPane->allRunControls(), [&projects, stopAll](RunControl *rc) { + return rc->isRunning() && (stopAll || projects.contains(rc->project())); + }); if (!toStop.isEmpty()) { bool stopThem = true; diff --git a/src/plugins/projectexplorer/projectwizardpage.cpp b/src/plugins/projectexplorer/projectwizardpage.cpp index 4c0d5f99c90..965897b1e3c 100644 --- a/src/plugins/projectexplorer/projectwizardpage.cpp +++ b/src/plugins/projectexplorer/projectwizardpage.cpp @@ -186,17 +186,22 @@ void BestNodeSelector::inspect(AddNewTree *tree, bool isContextNode) } if (m_deploys) return; + const QString projectDirectory = ProjectExplorerPlugin::directoryFor(node); const int projectDirectorySize = projectDirectory.size(); - if (!m_commonDirectory.startsWith(projectDirectory) && !isContextNode) + if (m_commonDirectory != projectDirectory + && !m_commonDirectory.startsWith(projectDirectory + QLatin1Char('/')) + && !isContextNode) return; - bool betterMatch = tree->priority() > 0 - && (projectDirectorySize > m_bestMatchLength - || (projectDirectorySize == m_bestMatchLength && tree->priority() > m_bestMatchPriority)); + + bool betterMatch = isContextNode + || (tree->priority() > 0 + && (projectDirectorySize > m_bestMatchLength + || (projectDirectorySize == m_bestMatchLength && tree->priority() > m_bestMatchPriority))); if (betterMatch) { m_bestMatchPriority = tree->priority(); - m_bestMatchLength = projectDirectorySize; + m_bestMatchLength = isContextNode ? std::numeric_limits::max() : projectDirectorySize; m_bestChoice = tree; } } @@ -268,7 +273,8 @@ static inline AddNewTree *buildAddProjectTree(SessionNode *root, const QString & return new AddNewTree(root, children, root->displayName()); } -static inline AddNewTree *buildAddFilesTree(FolderNode *root, const QStringList &files, Node *contextNode, BestNodeSelector *selector) +static inline AddNewTree *buildAddFilesTree(FolderNode *root, const QStringList &files, + Node *contextNode, BestNodeSelector *selector) { QList children; foreach (FolderNode *fn, root->subFolderNodes()) { @@ -289,7 +295,8 @@ static inline AddNewTree *buildAddFilesTree(FolderNode *root, const QStringList return new AddNewTree(root, children, root->displayName()); } -static inline AddNewTree *buildAddFilesTree(SessionNode *root, const QStringList &files, Node *contextNode, BestNodeSelector *selector) +static inline AddNewTree *buildAddFilesTree(SessionNode *root, const QStringList &files, + Node *contextNode, BestNodeSelector *selector) { QList children; foreach (ProjectNode *pn, root->projectNodes()) { diff --git a/src/plugins/projectexplorer/waitforstopdialog.h b/src/plugins/projectexplorer/waitforstopdialog.h index 42530caccc7..ed1f04956f9 100644 --- a/src/plugins/projectexplorer/waitforstopdialog.h +++ b/src/plugins/projectexplorer/waitforstopdialog.h @@ -46,6 +46,7 @@ namespace Internal { class WaitForStopDialog : public QDialog { + Q_OBJECT public: WaitForStopDialog(QList runControls); diff --git a/src/plugins/qbsprojectmanager/qbsnodes.cpp b/src/plugins/qbsprojectmanager/qbsnodes.cpp index e754153da76..154bd70da99 100644 --- a/src/plugins/qbsprojectmanager/qbsnodes.cpp +++ b/src/plugins/qbsprojectmanager/qbsnodes.cpp @@ -44,6 +44,7 @@ #include #include +#include #include #include @@ -141,7 +142,7 @@ public: return new FileTreeNode(n, this, isFile); } - bool isFile() { return m_isFile; } + bool isFile() const { return m_isFile; } static FileTreeNode *moveChildrenUp(FileTreeNode *node) { @@ -228,7 +229,7 @@ public: } } - QString path() + QString path() const { QString p = name; FileTreeNode *node = parent; @@ -485,7 +486,7 @@ void QbsGroupNode::setupFolder(ProjectExplorer::FolderNode *root, const qbs::Gro foreach (FileTreeNode *c, fileTree->children) { Utils::FileName path = Utils::FileName::fromString(c->path()); - const ProjectExplorer::FileType newFileType = fileType(group, c->path()); + const ProjectExplorer::FileType newFileType = fileType(group, *c); const bool isQrcFile = newFileType == ProjectExplorer::ResourceType; // Handle files: @@ -546,13 +547,17 @@ void QbsGroupNode::setupFolder(ProjectExplorer::FolderNode *root, const qbs::Gro } ProjectExplorer::FileType QbsGroupNode::fileType(const qbs::GroupData &group, - const QString &filePath) + const FileTreeNode &fileNode) { if (!group.isValid()) return ProjectExplorer::UnknownFileType; const qbs::SourceArtifact artifact = Utils::findOrDefault(group.allSourceArtifacts(), - [filePath](const qbs::SourceArtifact &sa) { return sa.filePath() == filePath; }); - QTC_ASSERT(artifact.isValid(), return ProjectExplorer::UnknownFileType); + [&fileNode](const qbs::SourceArtifact &sa) { return sa.filePath() == fileNode.path(); }); + QTC_ASSERT(artifact.isValid() || !fileNode.isFile(), + qDebug() << fileNode.path() << group.name(); return ProjectExplorer::UnknownFileType); + if (!artifact.isValid()) + return ProjectExplorer::UnknownFileType; + if (artifact.fileTags().contains(QLatin1String("c")) || artifact.fileTags().contains(QLatin1String("cpp")) || artifact.fileTags().contains(QLatin1String("objc")) diff --git a/src/plugins/qbsprojectmanager/qbsnodes.h b/src/plugins/qbsprojectmanager/qbsnodes.h index d3627713232..835945a7d94 100644 --- a/src/plugins/qbsprojectmanager/qbsnodes.h +++ b/src/plugins/qbsprojectmanager/qbsnodes.h @@ -113,7 +113,8 @@ public: private: static void setupFolder(ProjectExplorer::FolderNode *folder, const qbs::GroupData &group, const FileTreeNode *subFileTree, const QString &baseDir, bool updateExisting); - static ProjectExplorer::FileType fileType(const qbs::GroupData &group, const QString &filePath); + static ProjectExplorer::FileType fileType(const qbs::GroupData &group, + const FileTreeNode &fileNode); qbs::GroupData m_qbsGroupData; QString m_productPath; diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index ac7824e7684..ac5925d8a3a 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -773,7 +773,6 @@ static bool isBlacklistImport(const ImportKey &importKey) || importKey.libraryQualifiedPath() == QStringLiteral("QtQuick.Particles") //Unsupported || importKey.libraryQualifiedPath() == QStringLiteral("QtQuick.Dialogs") //Unsupported || importKey.libraryQualifiedPath() == QStringLiteral("QtQuick.Controls.Styles") //Unsupported - || importKey.libraryQualifiedPath().contains(QStringLiteral("Qt.labs")) //No support for labs plugins || importKey.libraryQualifiedPath() == QStringLiteral("QtNfc") //Unsupported || importKey.libraryQualifiedPath() == QStringLiteral("QtMultimedia") || importKey.libraryQualifiedPath() == QStringLiteral("Qt.WebSockets") diff --git a/src/plugins/qmlprofiler/qmlprofileroptionspage.h b/src/plugins/qmlprofiler/qmlprofileroptionspage.h index 5698902cd5f..635e3e85039 100644 --- a/src/plugins/qmlprofiler/qmlprofileroptionspage.h +++ b/src/plugins/qmlprofiler/qmlprofileroptionspage.h @@ -39,6 +39,7 @@ namespace Internal { class QmlProfilerOptionsPage : public Core::IOptionsPage { + Q_OBJECT public: QmlProfilerOptionsPage(); diff --git a/src/plugins/qmlprofiler/qmlprofilerrunconfigurationaspect.cpp b/src/plugins/qmlprofiler/qmlprofilerrunconfigurationaspect.cpp index 459ae255e9f..b2721302fc7 100644 --- a/src/plugins/qmlprofiler/qmlprofilerrunconfigurationaspect.cpp +++ b/src/plugins/qmlprofiler/qmlprofilerrunconfigurationaspect.cpp @@ -45,7 +45,7 @@ QmlProfilerRunConfigurationAspect::QmlProfilerRunConfigurationAspect( setProjectSettings(new QmlProfilerSettings()); setGlobalSettings(QmlProfilerPlugin::globalSettings()); setId(Constants::SETTINGS); - setDisplayName(tr("QML Profiler Settings")); + setDisplayName(QCoreApplication::translate("QmlProfilerRunConfiguration", "QML Profiler Settings")); setUsingGlobalSettings(true); resetProjectToGlobalSettings(); } diff --git a/src/plugins/qnx/qnx.pro b/src/plugins/qnx/qnx.pro index bd4aa5029d7..d4bfe94e9e7 100644 --- a/src/plugins/qnx/qnx.pro +++ b/src/plugins/qnx/qnx.pro @@ -34,7 +34,8 @@ SOURCES += qnxplugin.cpp \ qnxconfigurationmanager.cpp \ qnxsettingspage.cpp \ qnxversionnumber.cpp \ - qnxdeployqtlibrariesdialog.cpp + qnxdeployqtlibrariesdialog.cpp \ + qnxdeviceprocess.cpp HEADERS += qnxplugin.h\ qnxconstants.h \ @@ -69,14 +70,16 @@ HEADERS += qnxplugin.h\ qnxconfigurationmanager.h \ qnxsettingspage.h \ qnxversionnumber.h \ - qnxdeployqtlibrariesdialog.h + qnxdeployqtlibrariesdialog.h \ + qnx_export.h \ + qnxdeviceprocess.h FORMS += \ qnxsettingswidget.ui \ qnxdeployqtlibrariesdialog.ui -DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII +DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII QNX_LIBRARY RESOURCES += \ qnx.qrc diff --git a/src/plugins/qnx/qnx.qbs b/src/plugins/qnx/qnx.qbs index 3e3791166d1..39e9b343f3f 100644 --- a/src/plugins/qnx/qnx.qbs +++ b/src/plugins/qnx/qnx.qbs @@ -58,6 +58,8 @@ QtcPlugin { "qnxdeviceprocesslist.h", "qnxdeviceprocesssignaloperation.cpp", "qnxdeviceprocesssignaloperation.h", + "qnxdeviceprocess.cpp", + "qnxdeviceprocess.h", "qnxdevicetester.cpp", "qnxdevicetester.h", "qnxsettingswidget.cpp", @@ -85,6 +87,7 @@ QtcPlugin { "qnxruncontrolfactory.h", "qnxutils.cpp", "qnxutils.h", + "qnx_export.h", "slog2inforunner.cpp", "slog2inforunner.h", ] diff --git a/src/libs/utils/autoreleasepool.h b/src/plugins/qnx/qnx_export.h similarity index 81% rename from src/libs/utils/autoreleasepool.h rename to src/plugins/qnx/qnx_export.h index e60411443e6..46c321d5acb 100644 --- a/src/libs/utils/autoreleasepool.h +++ b/src/plugins/qnx/qnx_export.h @@ -28,24 +28,15 @@ ** ****************************************************************************/ +#ifndef QNX_EXPORT_H +#define QNX_EXPORT_H -#ifndef AUTORELEASEPOOL_H -#define AUTORELEASEPOOL_H +#include -#import - -namespace Utils { - -class AutoreleasePool -{ -public: - AutoreleasePool() { pool = [[NSAutoreleasePool alloc] init]; } - ~AutoreleasePool() { [pool release]; } -private: - NSAutoreleasePool *pool; -}; - -} // Utils - -#endif // AUTORELEASEPOOL_H +#if defined(QNX_LIBRARY) +# define QNX_EXPORT Q_DECL_EXPORT +#else +# define QNX_EXPORT Q_DECL_IMPORT +#endif +#endif // QNX_EXPORT_H diff --git a/src/plugins/qnx/qnxdeviceconfiguration.cpp b/src/plugins/qnx/qnxdeviceconfiguration.cpp index 06998bc8015..d24aae59709 100644 --- a/src/plugins/qnx/qnxdeviceconfiguration.cpp +++ b/src/plugins/qnx/qnxdeviceconfiguration.cpp @@ -35,11 +35,11 @@ #include "qnxdeviceprocesslist.h" #include "qnxdeviceprocesssignaloperation.h" #include "qnxdeployqtlibrariesdialog.h" +#include "qnxdeviceprocess.h" #include #include #include -#include #include #include @@ -50,51 +50,12 @@ using namespace ProjectExplorer; using namespace Utils; namespace Qnx { -namespace Internal { + +using namespace Internal; const char QnxVersionKey[] = "QnxVersion"; const char DeployQtLibrariesActionId [] = "Qnx.Qnx.DeployQtLibrariesAction"; -static int pidFileCounter = 0; - -QnxDeviceProcess::QnxDeviceProcess(const QSharedPointer &device, QObject *parent) - : SshDeviceProcess(device, parent) -{ - setEnvironment(Environment(OsTypeLinux)); - m_pidFile = QString::fromLatin1("/var/run/qtc.%1.pid").arg(++pidFileCounter); -} - -QString QnxDeviceProcess::fullCommandLine() const -{ - QStringList args = arguments(); - args.prepend(executable()); - QString cmd = QtcProcess::Arguments::createUnixArgs(args).toString(); - - QString fullCommandLine = QLatin1String( - "test -f /etc/profile && . /etc/profile ; " - "test -f $HOME/profile && . $HOME/profile ; " - ); - - if (!m_workingDir.isEmpty()) - fullCommandLine += QString::fromLatin1("cd %1 ; ").arg(QtcProcess::quoteArg(m_workingDir)); - - for (auto it = environment().constBegin(); it != environment().constEnd(); ++it) - fullCommandLine += QString::fromLatin1("%1='%2' ").arg(it.key()).arg(it.value()); - - fullCommandLine += QString::fromLatin1("%1 & echo $! > %2").arg(cmd).arg(m_pidFile); - - return fullCommandLine; -} - -void QnxDeviceProcess::doSignal(int sig) -{ - auto signaler = new SshDeviceProcess(device(), this); - QString cmd = QString::fromLatin1("kill -%2 `cat %1`").arg(m_pidFile).arg(sig); - connect(signaler, &SshDeviceProcess::finished, signaler, &QObject::deleteLater); - signaler->start(cmd, QStringList()); -} - - class QnxPortsGatheringMethod : public PortsGatheringMethod { // TODO: The command is probably needlessly complicated because the parsing method @@ -276,5 +237,4 @@ DeviceProcessSignalOperation::Ptr QnxDeviceConfiguration::signalOperation() cons new QnxDeviceProcessSignalOperation(sshParameters())); } -} // namespace Internal } // namespace Qnx diff --git a/src/plugins/qnx/qnxdeviceconfiguration.h b/src/plugins/qnx/qnxdeviceconfiguration.h index 212716a705c..22a904d8a9e 100644 --- a/src/plugins/qnx/qnxdeviceconfiguration.h +++ b/src/plugins/qnx/qnxdeviceconfiguration.h @@ -33,31 +33,13 @@ #ifndef QNX_INTERNAL_QNXDEVICECONFIGURATION_H #define QNX_INTERNAL_QNXDEVICECONFIGURATION_H +#include "qnx_export.h" + #include -#include namespace Qnx { -namespace Internal { -class QnxDeviceProcess : public ProjectExplorer::SshDeviceProcess -{ -public: - QnxDeviceProcess(const QSharedPointer &device, QObject *parent); - - void setWorkingDirectory(const QString &directory) { m_workingDir = directory; } - - void interrupt() { doSignal(2); } - void terminate() { doSignal(15); } - void kill() { doSignal(9); } - QString fullCommandLine() const; - -private: - void doSignal(int sig); - QString m_pidFile; - QString m_workingDir; -}; - -class QnxDeviceConfiguration : public RemoteLinux::LinuxDevice +class QNX_EXPORT QnxDeviceConfiguration : public RemoteLinux::LinuxDevice { Q_DECLARE_TR_FUNCTIONS(Qnx::Internal::QnxDeviceConfiguration) @@ -103,7 +85,6 @@ private: mutable int m_versionNumber; }; -} // namespace Internal } // namespace Qnx #endif // QNX_INTERNAL_QNXDEVICECONFIGURATION_H diff --git a/src/plugins/qnx/qnxdeviceprocess.cpp b/src/plugins/qnx/qnxdeviceprocess.cpp new file mode 100644 index 00000000000..c13cffb1b0d --- /dev/null +++ b/src/plugins/qnx/qnxdeviceprocess.cpp @@ -0,0 +1,84 @@ +/************************************************************************** +** +** Copyright (C) 2012 - 2014 BlackBerry Limited. All rights reserved. +** +** Contact: BlackBerry (qt@blackberry.com) +** Contact: KDAB (info@kdab.com) +** +** This file is part of Qt Creator. +** +** 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 http://www.qt.io/terms-conditions. For further information +** use the contact form at http://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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "qnxdeviceprocess.h" + +#include +#include + +using namespace ProjectExplorer; +using namespace Utils; + +namespace Qnx { +namespace Internal { + +static int pidFileCounter = 0; + +QnxDeviceProcess::QnxDeviceProcess(const QSharedPointer &device, QObject *parent) + : SshDeviceProcess(device, parent) +{ + setEnvironment(Environment(OsTypeLinux)); + m_pidFile = QString::fromLatin1("/var/run/qtc.%1.pid").arg(++pidFileCounter); +} + +QString QnxDeviceProcess::fullCommandLine() const +{ + QStringList args = arguments(); + args.prepend(executable()); + QString cmd = QtcProcess::Arguments::createUnixArgs(args).toString(); + + QString fullCommandLine = QLatin1String( + "test -f /etc/profile && . /etc/profile ; " + "test -f $HOME/profile && . $HOME/profile ; " + ); + + if (!m_workingDir.isEmpty()) + fullCommandLine += QString::fromLatin1("cd %1 ; ").arg(QtcProcess::quoteArg(m_workingDir)); + + for (auto it = environment().constBegin(); it != environment().constEnd(); ++it) + fullCommandLine += QString::fromLatin1("%1='%2' ").arg(it.key()).arg(it.value()); + + fullCommandLine += QString::fromLatin1("%1 & echo $! > %2").arg(cmd).arg(m_pidFile); + + return fullCommandLine; +} + +void QnxDeviceProcess::doSignal(int sig) +{ + auto signaler = new SshDeviceProcess(device(), this); + QString cmd = QString::fromLatin1("kill -%2 `cat %1`").arg(m_pidFile).arg(sig); + connect(signaler, &SshDeviceProcess::finished, signaler, &QObject::deleteLater); + signaler->start(cmd, QStringList()); +} + +} // namespace Internal +} // namespace Qnx diff --git a/src/plugins/qnx/qnxdeviceprocess.h b/src/plugins/qnx/qnxdeviceprocess.h new file mode 100644 index 00000000000..311257a0f40 --- /dev/null +++ b/src/plugins/qnx/qnxdeviceprocess.h @@ -0,0 +1,64 @@ +/************************************************************************** +** +** Copyright (C) 2012 - 2014 BlackBerry Limited. All rights reserved. +** +** Contact: BlackBerry (qt@blackberry.com) +** Contact: KDAB (info@kdab.com) +** +** This file is part of Qt Creator. +** +** 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 http://www.qt.io/terms-conditions. For further information +** use the contact form at http://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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef QNXDEVICEPROCESS_H +#define QNXDEVICEPROCESS_H + +#include "qnx_export.h" +#include +#include + +namespace Qnx { +namespace Internal { + +class QnxDeviceProcess : public ProjectExplorer::SshDeviceProcess +{ +public: + QnxDeviceProcess(const QSharedPointer &device, QObject *parent); + + void setWorkingDirectory(const QString &directory) { m_workingDir = directory; } + + void interrupt() { doSignal(2); } + void terminate() { doSignal(15); } + void kill() { doSignal(9); } + QString fullCommandLine() const; + +private: + void doSignal(int sig); + QString m_pidFile; + QString m_workingDir; +}; + +} // namespace Internal +} // namespace Qnx + +#endif // QNXDEVICEPROCESS_H diff --git a/src/plugins/qnx/qnxdeviceprocesssignaloperation.h b/src/plugins/qnx/qnxdeviceprocesssignaloperation.h index 99b64324977..3d313ba6348 100644 --- a/src/plugins/qnx/qnxdeviceprocesssignaloperation.h +++ b/src/plugins/qnx/qnxdeviceprocesssignaloperation.h @@ -36,6 +36,8 @@ #include namespace Qnx { +class QnxDeviceConfiguration; + namespace Internal { class QnxDeviceProcessSignalOperation : public RemoteLinux::RemoteLinuxSignalOperation @@ -48,7 +50,7 @@ private: QString killProcessByNameCommandLine(const QString &filePath) const; QString interruptProcessByNameCommandLine(const QString &filePath) const; - friend class QnxDeviceConfiguration; + friend class Qnx::QnxDeviceConfiguration; }; } // namespace Internal diff --git a/src/plugins/qnx/slog2inforunner.cpp b/src/plugins/qnx/slog2inforunner.cpp index 504ea6c0517..4eea0200a76 100644 --- a/src/plugins/qnx/slog2inforunner.cpp +++ b/src/plugins/qnx/slog2inforunner.cpp @@ -32,7 +32,7 @@ #include "slog2inforunner.h" -#include "qnxdeviceconfiguration.h" +#include "qnxdeviceprocess.h" #include diff --git a/src/plugins/texteditor/findinfiles.cpp b/src/plugins/texteditor/findinfiles.cpp index 5043682181f..fc33b2cb499 100644 --- a/src/plugins/texteditor/findinfiles.cpp +++ b/src/plugins/texteditor/findinfiles.cpp @@ -71,7 +71,7 @@ QString FindInFiles::id() const QString FindInFiles::displayName() const { - return tr("Files on File System"); + return tr("Files in File System"); } void FindInFiles::findAll(const QString &txt, FindFlags findFlags) diff --git a/src/shared/json/README.md b/src/shared/json/README.md new file mode 100644 index 00000000000..e641d8e5fe2 --- /dev/null +++ b/src/shared/json/README.md @@ -0,0 +1,3 @@ +This is QJson without Qt, to be used in circumstances +where a Qt dependency is not desirable, such as +qtcreatorcdbex. diff --git a/src/shared/json/json.cpp b/src/shared/json/json.cpp new file mode 100644 index 00000000000..c40aa3dc1f9 --- /dev/null +++ b/src/shared/json/json.cpp @@ -0,0 +1,4956 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "json.h" + + +//#define PARSER_DEBUG +#ifdef PARSER_DEBUG +static int indent = 0; +#define BEGIN std::cerr << std::string(4*indent++, ' ').data() << " pos=" << current +#define END --indent +#define DEBUG std::cerr << std::string(4*indent, ' ').data() +#else +#define BEGIN if (1) ; else std::cerr +#define END do {} while (0) +#define DEBUG if (1) ; else std::cerr +#endif + +static const int nestingLimit = 1024; + + +namespace Json { +namespace Internal { + +/* + This defines a binary data structure for Json data. The data structure is optimised for fast reading + and minimum allocations. The whole data structure can be mmap'ed and used directly. + + In most cases the binary structure is not as space efficient as a utf8 encoded text representation, but + much faster to access. + + The size requirements are: + + String: 4 bytes header + 2*(string.length()) + + Values: 4 bytes + size of data (size can be 0 for some data) + bool: 0 bytes + double: 8 bytes (0 if integer with less than 27bits) + string: see above + array: size of array + object: size of object + Array: 12 bytes + 4*length + size of Value data + Object: 12 bytes + 8*length + size of Key Strings + size of Value data + + For an example such as + + { // object: 12 + 5*8 = 52 + "firstName": "John", // key 12, value 8 = 20 + "lastName" : "Smith", // key 12, value 8 = 20 + "age" : 25, // key 8, value 0 = 8 + "address" : // key 12, object below = 140 + { // object: 12 + 4*8 + "streetAddress": "21 2nd Street", // key 16, value 16 + "city" : "New York", // key 8, value 12 + "state" : "NY", // key 8, value 4 + "postalCode" : "10021" // key 12, value 8 + }, // object total: 128 + "phoneNumber": // key: 16, value array below = 172 + [ // array: 12 + 2*4 + values below: 156 + { // object 12 + 2*8 + "type" : "home", // key 8, value 8 + "number": "212 555-1234" // key 8, value 16 + }, // object total: 68 + { // object 12 + 2*8 + "type" : "fax", // key 8, value 8 + "number": "646 555-4567" // key 8, value 16 + } // object total: 68 + ] // array total: 156 + } // great total: 412 bytes + + The uncompressed text file used roughly 500 bytes, so in this case we end up using about + the same space as the text representation. + + Other measurements have shown a slightly bigger binary size than a compact text + representation where all possible whitespace was stripped out. +*/ + +class Array; +class Object; +class Value; +class Entry; + +template +class qle_bitfield +{ +public: + uint32_t val; + + enum { + mask = ((1u << width) - 1) << pos + }; + + void operator=(uint32_t t) { + uint32_t i = val; + i &= ~mask; + i |= t << pos; + val = i; + } + operator uint32_t() const { + uint32_t t = val; + t &= mask; + t >>= pos; + return t; + } + bool operator!() const { return !operator uint32_t(); } + bool operator==(uint32_t t) { return uint32_t(*this) == t; } + bool operator!=(uint32_t t) { return uint32_t(*this) != t; } + bool operator<(uint32_t t) { return uint32_t(*this) < t; } + bool operator>(uint32_t t) { return uint32_t(*this) > t; } + bool operator<=(uint32_t t) { return uint32_t(*this) <= t; } + bool operator>=(uint32_t t) { return uint32_t(*this) >= t; } + void operator+=(uint32_t i) { *this = (uint32_t(*this) + i); } + void operator-=(uint32_t i) { *this = (uint32_t(*this) - i); } +}; + +template +class qle_signedbitfield +{ +public: + uint32_t val; + + enum { + mask = ((1u << width) - 1) << pos + }; + + void operator=(int t) { + uint32_t i = val; + i &= ~mask; + i |= t << pos; + val = i; + } + operator int() const { + uint32_t i = val; + i <<= 32 - width - pos; + int t = (int) i; + t >>= pos; + return t; + } + + bool operator!() const { return !operator int(); } + bool operator==(int t) { return int(*this) == t; } + bool operator!=(int t) { return int(*this) != t; } + bool operator<(int t) { return int(*this) < t; } + bool operator>(int t) { return int(*this) > t; } + bool operator<=(int t) { return int(*this) <= t; } + bool operator>=(int t) { return int(*this) >= t; } + void operator+=(int i) { *this = (int(*this) + i); } + void operator-=(int i) { *this = (int(*this) - i); } +}; + +typedef uint32_t offset; + +// round the size up to the next 4 byte boundary +int alignedSize(int size) { return (size + 3) & ~3; } + +static int qStringSize(const std::string &ba) +{ + int l = 4 + ba.length(); + return alignedSize(l); +} + +// returns INT_MAX if it can't compress it into 28 bits +static int compressedNumber(double d) +{ + // this relies on details of how ieee floats are represented + const int exponent_off = 52; + const uint64_t fraction_mask = 0x000fffffffffffffull; + const uint64_t exponent_mask = 0x7ff0000000000000ull; + + uint64_t val; + memcpy (&val, &d, sizeof(double)); + int exp = (int)((val & exponent_mask) >> exponent_off) - 1023; + if (exp < 0 || exp > 25) + return INT_MAX; + + uint64_t non_int = val & (fraction_mask >> exp); + if (non_int) + return INT_MAX; + + bool neg = (val >> 63) != 0; + val &= fraction_mask; + val |= ((uint64_t)1 << 52); + int res = (int)(val >> (52 - exp)); + return neg ? -res : res; +} + +static void toInternal(char *addr, const char *data, int size) +{ + memcpy(addr, &size, 4); + memcpy(addr + 4, data, size); +} + +class String +{ +public: + String(const char *data) { d = (Data *)data; } + + struct Data { + int length; + char utf8[1]; + }; + + Data *d; + + void operator=(const std::string &ba) + { + d->length = ba.length(); + memcpy(d->utf8, ba.data(), ba.length()); + } + + bool operator==(const std::string &ba) const { + return toString() == ba; + } + bool operator!=(const std::string &str) const { + return !operator==(str); + } + bool operator>=(const std::string &str) const { + // ### + return toString() >= str; + } + + bool operator==(const String &str) const { + if (d->length != str.d->length) + return false; + return !memcmp(d->utf8, str.d->utf8, d->length); + } + bool operator<(const String &other) const; + bool operator>=(const String &other) const { return !(*this < other); } + + std::string toString() const { + return std::string(d->utf8, d->length); + } + +}; + +bool String::operator<(const String &other) const +{ + int alen = d->length; + int blen = other.d->length; + int l = std::min(alen, blen); + char *a = d->utf8; + char *b = other.d->utf8; + + while (l-- && *a == *b) + a++,b++; + if (l==-1) + return (alen < blen); + return (unsigned char)(*a) < (unsigned char)(*b); +} + +static void copyString(char *dest, const std::string &str) +{ + String string(dest); + string = str; +} + + +/* + Base is the base class for both Object and Array. Both classe work more or less the same way. + The class starts with a header (defined by the struct below), then followed by data (the data for + values in the Array case and Entry's (see below) for objects. + + After the data a table follows (tableOffset points to it) containing Value objects for Arrays, and + offsets from the beginning of the object to Entry's in the case of Object. + + Entry's in the Object's table are lexicographically sorted by key in the table(). This allows the usage + of a binary search over the keys in an Object. + */ +class Base +{ +public: + uint32_t size; + union { + uint32_t _dummy; + qle_bitfield<0, 1> is_object; + qle_bitfield<1, 31> length; + }; + offset tableOffset; + // content follows here + + bool isObject() const { return !!is_object; } + bool isArray() const { return !isObject(); } + + offset *table() const { return (offset *) (((char *) this) + tableOffset); } + + int reserveSpace(uint32_t dataSize, int posInTable, uint32_t numItems, bool replace); + void removeItems(int pos, int numItems); +}; + +class Object : public Base +{ +public: + Entry *entryAt(int i) const { + return reinterpret_cast(((char *)this) + table()[i]); + } + int indexOf(const std::string &key, bool *exists); + + bool isValid() const; +}; + + +class Value +{ +public: + enum { + MaxSize = (1<<27) - 1 + }; + union { + uint32_t _dummy; + qle_bitfield<0, 3> type; + qle_bitfield<3, 1> intValue; + qle_bitfield<4, 1> _; // Ex-latin1Key + qle_bitfield<5, 27> value; // Used as offset in case of Entry(?) + qle_signedbitfield<5, 27> int_value; + }; + + char *data(const Base *b) const { return ((char *)b) + value; } + int usedStorage(const Base *b) const; + + bool toBoolean() const { return value != 0; } + double toDouble(const Base *b) const; + std::string toString(const Base *b) const; + Base *base(const Base *b) const; + + bool isValid(const Base *b) const; + + static int requiredStorage(JsonValue &v, bool *compressed); + static uint32_t valueToStore(const JsonValue &v, uint32_t offset); + static void copyData(const JsonValue &v, char *dest, bool compressed); +}; + +class Array : public Base +{ +public: + Value at(int i) const { return *(Value *) (table() + i); } + Value &operator[](int i) { return *(Value *) (table() + i); } + + bool isValid() const; +}; + +class Entry { +public: + Value value; + // key + // value data follows key + + int size() const + { + int s = sizeof(Entry); + s += sizeof(uint32_t) + (*(int *) ((const char *)this + sizeof(Entry))); + return alignedSize(s); + } + + int usedStorage(Base *b) const + { + return size() + value.usedStorage(b); + } + + String shallowKey() const + { + return String((const char *)this + sizeof(Entry)); + } + + std::string key() const + { + return shallowKey().toString(); + } + + bool operator==(const std::string &key) const; + bool operator!=(const std::string &key) const { return !operator==(key); } + bool operator>=(const std::string &key) const { return shallowKey() >= key; } + + bool operator==(const Entry &other) const; + bool operator>=(const Entry &other) const; +}; + +bool operator<(const std::string &key, const Entry &e) +{ + return e >= key; +} + + +class Header +{ +public: + uint32_t tag; // 'qbjs' + uint32_t version; // 1 + Base *root() { return (Base *)(this + 1); } +}; + + +double Value::toDouble(const Base *b) const +{ + // assert(type == JsonValue::Double); + if (intValue) + return int_value; + + double d; + memcpy(&d, (const char *)b + value, 8); + return d; +} + +std::string Value::toString(const Base *b) const +{ + String s(data(b)); + return s.toString(); +} + +Base *Value::base(const Base *b) const +{ + // assert(type == JsonValue::Array || type == JsonValue::Object); + return reinterpret_cast(data(b)); +} + +class AtomicInt +{ +public: + bool ref() { return ++x != 0; } + bool deref() { return --x != 0; } + int load() { return x.load(std::memory_order_seq_cst); } +private: + std::atomic x { 0 }; +}; + + +class SharedString +{ +public: + AtomicInt ref; + std::string s; +}; + +class Data { +public: + enum Validation { + Unchecked, + Validated, + Invalid + }; + + AtomicInt ref; + int alloc; + union { + char *rawData; + Header *header; + }; + uint32_t compactionCounter : 31; + uint32_t ownsData : 1; + + Data(char *raw, int a) + : alloc(a), rawData(raw), compactionCounter(0), ownsData(true) + { + } + Data(int reserved, JsonValue::Type valueType) + : rawData(0), compactionCounter(0), ownsData(true) + { + // assert(valueType == JsonValue::Array || valueType == JsonValue::Object); + + alloc = sizeof(Header) + sizeof(Base) + reserved + sizeof(offset); + header = (Header *)malloc(alloc); + header->tag = JsonDocument::BinaryFormatTag; + header->version = 1; + Base *b = header->root(); + b->size = sizeof(Base); + b->is_object = (valueType == JsonValue::Object); + b->tableOffset = sizeof(Base); + b->length = 0; + } + ~Data() + { if (ownsData) free(rawData); } + + uint32_t offsetOf(const void *ptr) const { return (uint32_t)(((char *)ptr - rawData)); } + + JsonObject toObject(Object *o) const + { + return JsonObject(const_cast(this), o); + } + + JsonArray toArray(Array *a) const + { + return JsonArray(const_cast(this), a); + } + + Data *clone(Base *b, int reserve = 0) + { + int size = sizeof(Header) + b->size; + if (b == header->root() && ref.load() == 1 && alloc >= size + reserve) + return this; + + if (reserve) { + if (reserve < 128) + reserve = 128; + size = std::max(size + reserve, size *2); + } + char *raw = (char *)malloc(size); + memcpy(raw + sizeof(Header), b, b->size); + Header *h = (Header *)raw; + h->tag = JsonDocument::BinaryFormatTag; + h->version = 1; + Data *d = new Data(raw, size); + d->compactionCounter = (b == header->root()) ? compactionCounter : 0; + return d; + } + + void compact(); + bool valid() const; + +private: + Data(const Data &); + void operator=(const Data &); +}; + + +void objectToJson(const Object *o, std::string &json, int indent, bool compact = false); +void arrayToJson(const Array *a, std::string &json, int indent, bool compact = false); + +class Parser +{ +public: + Parser(const char *json, int length); + + JsonDocument parse(JsonParseError *error); + + class ParsedObject + { + public: + ParsedObject(Parser *p, int pos) : parser(p), objectPosition(pos) { + offsets.reserve(64); + } + void insert(uint32_t offset); + + Parser *parser; + int objectPosition; + std::vector offsets; + + Entry *entryAt(int i) const { + return reinterpret_cast(parser->data + objectPosition + offsets[i]); + } + }; + + +private: + void eatBOM(); + bool eatSpace(); + char nextToken(); + + bool parseObject(); + bool parseArray(); + bool parseMember(int baseOffset); + bool parseString(); + bool parseEscapeSequence(); + bool parseValue(Value *val, int baseOffset); + bool parseNumber(Value *val, int baseOffset); + + void addChar(char c) { + const int pos = reserveSpace(1); + data[pos] = c; + } + + const char *head; + const char *json; + const char *end; + + char *data; + int dataLength; + int current; + int nestingLevel; + JsonParseError::ParseError lastError; + + int reserveSpace(int space) { + if (current + space >= dataLength) { + dataLength = 2*dataLength + space; + data = (char *)realloc(data, dataLength); + } + int pos = current; + current += space; + return pos; + } +}; + +} // namespace Internal + +using namespace Internal; + +/*! + \class JsonValue + \inmodule QtCore + \ingroup json + \ingroup shared + \reentrant + \since 5.0 + + \brief The JsonValue class encapsulates a value in JSON. + + A value in JSON can be one of 6 basic types: + + JSON is a format to store structured data. It has 6 basic data types: + + \list + \li bool JsonValue::Bool + \li double JsonValue::Double + \li string JsonValue::String + \li array JsonValue::Array + \li object JsonValue::Object + \li null JsonValue::Null + \endlist + + A value can represent any of the above data types. In addition, JsonValue has one special + flag to represent undefined values. This can be queried with isUndefined(). + + The type of the value can be queried with type() or accessors like isBool(), isString(), and so on. + Likewise, the value can be converted to the type stored in it using the toBool(), toString() and so on. + + Values are strictly typed internally and contrary to QVariant will not attempt to do any implicit type + conversions. This implies that converting to a type that is not stored in the value will return a default + constructed return value. + + \section1 JsonValueRef + + JsonValueRef is a helper class for JsonArray and JsonObject. + When you get an object of type JsonValueRef, you can + use it as if it were a reference to a JsonValue. If you assign to it, + the assignment will apply to the element in the JsonArray or JsonObject + from which you got the reference. + + The following methods return JsonValueRef: + \list + \li \l {JsonArray}::operator[](int i) + \li \l {JsonObject}::operator[](const QString & key) const + \endlist + + \sa {JSON Support in Qt}, {JSON Save Game Example} +*/ + +/*! + Creates a JsonValue of type \a type. + + The default is to create a Null value. + */ +JsonValue::JsonValue(Type type) + : ui(0), d(0), t(type) +{ +} + +/*! + \internal + */ +JsonValue::JsonValue(Internal::Data *data, Internal::Base *base, const Internal::Value &v) + : d(0), t((Type)(uint32_t)v.type) +{ + switch (t) { + case Undefined: + case Null: + dbl = 0; + break; + case Bool: + b = v.toBoolean(); + break; + case Double: + dbl = v.toDouble(base); + break; + case String: { + stringData = new Internal::SharedString; + stringData->s = v.toString(base); + stringData->ref.ref(); + break; + } + case Array: + case Object: + d = data; + this->base = v.base(base); + break; + } + if (d) + d->ref.ref(); +} + +/*! + Creates a value of type Bool, with value \a b. + */ +JsonValue::JsonValue(bool b) + : d(0), t(Bool) +{ + this->b = b; +} + +/*! + Creates a value of type Double, with value \a n. + */ +JsonValue::JsonValue(double n) + : d(0), t(Double) +{ + this->dbl = n; +} + +/*! + \overload + Creates a value of type Double, with value \a n. + */ +JsonValue::JsonValue(int n) + : d(0), t(Double) +{ + this->dbl = n; +} + +/*! + \overload + Creates a value of type Double, with value \a n. + NOTE: the integer limits for IEEE 754 double precision data is 2^53 (-9007199254740992 to +9007199254740992). + If you pass in values outside this range expect a loss of precision to occur. + */ +JsonValue::JsonValue(int64_t n) + : d(0), t(Double) +{ + this->dbl = double(n); +} + +/*! + Creates a value of type String, with value \a s. + */ +JsonValue::JsonValue(const std::string &s) + : d(0), t(String) +{ + stringData = new Internal::SharedString; + stringData->s = s; + stringData->ref.ref(); +} + +JsonValue::JsonValue(const char *s) + : d(0), t(String) +{ + stringData = new Internal::SharedString; + stringData->s = s; + stringData->ref.ref(); +} + +/*! + Creates a value of type Array, with value \a a. + */ +JsonValue::JsonValue(const JsonArray &a) + : d(a.d), t(Array) +{ + base = a.a; + if (d) + d->ref.ref(); +} + +/*! + Creates a value of type Object, with value \a o. + */ +JsonValue::JsonValue(const JsonObject &o) + : d(o.d), t(Object) +{ + base = o.o; + if (d) + d->ref.ref(); +} + + +/*! + Destroys the value. + */ +JsonValue::~JsonValue() +{ + if (t == String && stringData && !stringData->ref.deref()) + free(stringData); + + if (d && !d->ref.deref()) + delete d; +} + +/*! + Creates a copy of \a other. + */ +JsonValue::JsonValue(const JsonValue &other) + : t(other.t) +{ + d = other.d; + ui = other.ui; + if (d) + d->ref.ref(); + + if (t == String && stringData) + stringData->ref.ref(); +} + +/*! + Assigns the value stored in \a other to this object. + */ +JsonValue &JsonValue::operator=(const JsonValue &other) +{ + if (t == String && stringData && !stringData->ref.deref()) + free(stringData); + + t = other.t; + dbl = other.dbl; + + if (d != other.d) { + + if (d && !d->ref.deref()) + delete d; + d = other.d; + if (d) + d->ref.ref(); + + } + + if (t == String && stringData) + stringData->ref.ref(); + + return *this; +} + +/*! + \fn bool JsonValue::isNull() const + + Returns \c true if the value is null. +*/ + +/*! + \fn bool JsonValue::isBool() const + + Returns \c true if the value contains a boolean. + + \sa toBool() + */ + +/*! + \fn bool JsonValue::isDouble() const + + Returns \c true if the value contains a double. + + \sa toDouble() + */ + +/*! + \fn bool JsonValue::isString() const + + Returns \c true if the value contains a string. + + \sa toString() + */ + +/*! + \fn bool JsonValue::isArray() const + + Returns \c true if the value contains an array. + + \sa toArray() + */ + +/*! + \fn bool JsonValue::isObject() const + + Returns \c true if the value contains an object. + + \sa toObject() + */ + +/*! + \fn bool JsonValue::isUndefined() const + + Returns \c true if the value is undefined. This can happen in certain + error cases as e.g. accessing a non existing key in a JsonObject. + */ + + +/*! + \enum JsonValue::Type + + This enum describes the type of the JSON value. + + \value Null A Null value + \value Bool A boolean value. Use toBool() to convert to a bool. + \value Double A double. Use toDouble() to convert to a double. + \value String A string. Use toString() to convert to a QString. + \value Array An array. Use toArray() to convert to a JsonArray. + \value Object An object. Use toObject() to convert to a JsonObject. + \value Undefined The value is undefined. This is usually returned as an + error condition, when trying to read an out of bounds value + in an array or a non existent key in an object. +*/ + +/*! + Returns the type of the value. + + \sa JsonValue::Type + */ + + +/*! + Converts the value to a bool and returns it. + + If type() is not bool, the \a defaultValue will be returned. + */ +bool JsonValue::toBool(bool defaultValue) const +{ + if (t != Bool) + return defaultValue; + return b; +} + +/*! + Converts the value to an int and returns it. + + If type() is not Double or the value is not a whole number, + the \a defaultValue will be returned. + */ +int JsonValue::toInt(int defaultValue) const +{ + if (t == Double && int(dbl) == dbl) + return int(dbl); + return defaultValue; +} + +/*! + Converts the value to a double and returns it. + + If type() is not Double, the \a defaultValue will be returned. + */ +double JsonValue::toDouble(double defaultValue) const +{ + if (t != Double) + return defaultValue; + return dbl; +} + +/*! + Converts the value to a QString and returns it. + + If type() is not String, the \a defaultValue will be returned. + */ +std::string JsonValue::toString(const std::string &defaultValue) const +{ + if (t != String) + return defaultValue; + return stringData->s; +} + +/*! + Converts the value to an array and returns it. + + If type() is not Array, the \a defaultValue will be returned. + */ +JsonArray JsonValue::toArray(const JsonArray &defaultValue) const +{ + if (!d || t != Array) + return defaultValue; + + return JsonArray(d, static_cast(base)); +} + +/*! + \overload + + Converts the value to an array and returns it. + + If type() is not Array, a \l{JsonArray::}{JsonArray()} will be returned. + */ +JsonArray JsonValue::toArray() const +{ + return toArray(JsonArray()); +} + +/*! + Converts the value to an object and returns it. + + If type() is not Object, the \a defaultValue will be returned. + */ +JsonObject JsonValue::toObject(const JsonObject &defaultValue) const +{ + if (!d || t != Object) + return defaultValue; + + return JsonObject(d, static_cast(base)); +} + +/*! + \overload + + Converts the value to an object and returns it. + + If type() is not Object, the \l {JsonObject::}{JsonObject()} will be returned. +*/ +JsonObject JsonValue::toObject() const +{ + return toObject(JsonObject()); +} + +/*! + Returns \c true if the value is equal to \a other. + */ +bool JsonValue::operator==(const JsonValue &other) const +{ + if (t != other.t) + return false; + + switch (t) { + case Undefined: + case Null: + break; + case Bool: + return b == other.b; + case Double: + return dbl == other.dbl; + case String: + return toString() == other.toString(); + case Array: + if (base == other.base) + return true; + if (!base) + return !other.base->length; + if (!other.base) + return !base->length; + return JsonArray(d, static_cast(base)) + == JsonArray(other.d, static_cast(other.base)); + case Object: + if (base == other.base) + return true; + if (!base) + return !other.base->length; + if (!other.base) + return !base->length; + return JsonObject(d, static_cast(base)) + == JsonObject(other.d, static_cast(other.base)); + } + return true; +} + +/*! + Returns \c true if the value is not equal to \a other. + */ +bool JsonValue::operator!=(const JsonValue &other) const +{ + return !(*this == other); +} + +/*! + \internal + */ +void JsonValue::detach() +{ + if (!d) + return; + + Internal::Data *x = d->clone(base); + x->ref.ref(); + if (!d->ref.deref()) + delete d; + d = x; + base = static_cast(d->header->root()); +} + + +/*! + \class JsonValueRef + \inmodule QtCore + \reentrant + \brief The JsonValueRef class is a helper class for JsonValue. + + \internal + + \ingroup json + + When you get an object of type JsonValueRef, if you can assign to it, + the assignment will apply to the character in the string from + which you got the reference. That is its whole purpose in life. + + You can use it exactly in the same way as a reference to a JsonValue. + + The JsonValueRef becomes invalid once modifications are made to the + string: if you want to keep the character, copy it into a JsonValue. + + Most of the JsonValue member functions also exist in JsonValueRef. + However, they are not explicitly documented here. +*/ + + +JsonValueRef &JsonValueRef::operator=(const JsonValue &val) +{ + if (is_object) + o->setValueAt(index, val); + else + a->replace(index, val); + + return *this; +} + +JsonValueRef &JsonValueRef::operator=(const JsonValueRef &ref) +{ + if (is_object) + o->setValueAt(index, ref); + else + a->replace(index, ref); + + return *this; +} + +JsonArray JsonValueRef::toArray() const +{ + return toValue().toArray(); +} + +JsonObject JsonValueRef::toObject() const +{ + return toValue().toObject(); +} + +JsonValue JsonValueRef::toValue() const +{ + if (!is_object) + return a->at(index); + return o->valueAt(index); +} + +/*! + \class JsonArray + \inmodule QtCore + \ingroup json + \ingroup shared + \reentrant + \since 5.0 + + \brief The JsonArray class encapsulates a JSON array. + + A JSON array is a list of values. The list can be manipulated by inserting and + removing JsonValue's from the array. + + A JsonArray can be converted to and from a QVariantList. You can query the + number of entries with size(), insert(), and removeAt() entries from it + and iterate over its content using the standard C++ iterator pattern. + + JsonArray is an implicitly shared class and shares the data with the document + it has been created from as long as it is not being modified. + + You can convert the array to and from text based JSON through JsonDocument. + + \sa {JSON Support in Qt}, {JSON Save Game Example} +*/ + +/*! + \typedef JsonArray::Iterator + + Qt-style synonym for JsonArray::iterator. +*/ + +/*! + \typedef JsonArray::ConstIterator + + Qt-style synonym for JsonArray::const_iterator. +*/ + +/*! + \typedef JsonArray::size_type + + Typedef for int. Provided for STL compatibility. +*/ + +/*! + \typedef JsonArray::value_type + + Typedef for JsonValue. Provided for STL compatibility. +*/ + +/*! + \typedef JsonArray::difference_type + + Typedef for int. Provided for STL compatibility. +*/ + +/*! + \typedef JsonArray::pointer + + Typedef for JsonValue *. Provided for STL compatibility. +*/ + +/*! + \typedef JsonArray::const_pointer + + Typedef for const JsonValue *. Provided for STL compatibility. +*/ + +/*! + \typedef JsonArray::reference + + Typedef for JsonValue &. Provided for STL compatibility. +*/ + +/*! + \typedef JsonArray::const_reference + + Typedef for const JsonValue &. Provided for STL compatibility. +*/ + +/*! + Creates an empty array. + */ +JsonArray::JsonArray() + : d(0), a(0) +{ +} + +JsonArray::JsonArray(std::initializer_list args) + : d(0), a(0) +{ + for (auto i = args.begin(); i != args.end(); ++i) + append(*i); +} + +/*! + \fn JsonArray::JsonArray(std::initializer_list args) + \since 5.4 + Creates an array initialized from \a args initialization list. + + JsonArray can be constructed in a way similar to JSON notation, + for example: + \code + JsonArray array = { 1, 2.2, QString() }; + \endcode + */ + +/*! + \internal + */ +JsonArray::JsonArray(Internal::Data *data, Internal::Array *array) + : d(data), a(array) +{ + // assert(data); + // assert(array); + d->ref.ref(); +} + +/*! + Deletes the array. + */ +JsonArray::~JsonArray() +{ + if (d && !d->ref.deref()) + delete d; +} + +/*! + Creates a copy of \a other. + + Since JsonArray is implicitly shared, the copy is shallow + as long as the object doesn't get modified. + */ +JsonArray::JsonArray(const JsonArray &other) +{ + d = other.d; + a = other.a; + if (d) + d->ref.ref(); +} + +/*! + Assigns \a other to this array. + */ +JsonArray &JsonArray::operator=(const JsonArray &other) +{ + if (d != other.d) { + if (d && !d->ref.deref()) + delete d; + d = other.d; + if (d) + d->ref.ref(); + } + a = other.a; + + return *this; +} + +/*! \fn JsonArray &JsonArray::operator+=(const JsonValue &value) + + Appends \a value to the array, and returns a reference to the array itself. + + \since 5.3 + \sa append(), operator<<() +*/ + +/*! \fn JsonArray JsonArray::operator+(const JsonValue &value) const + + Returns an array that contains all the items in this array followed + by the provided \a value. + + \since 5.3 + \sa operator+=() +*/ + +/*! \fn JsonArray &JsonArray::operator<<(const JsonValue &value) + + Appends \a value to the array, and returns a reference to the array itself. + + \since 5.3 + \sa operator+=(), append() +*/ + +/*! + Returns the number of values stored in the array. + */ +int JsonArray::size() const +{ + if (!d) + return 0; + + return (int)a->length; +} + +/*! + \fn JsonArray::count() const + + Same as size(). + + \sa size() +*/ + +/*! + Returns \c true if the object is empty. This is the same as size() == 0. + + \sa size() + */ +bool JsonArray::isEmpty() const +{ + if (!d) + return true; + + return !a->length; +} + +/*! + Returns a JsonValue representing the value for index \a i. + + The returned JsonValue is \c Undefined, if \a i is out of bounds. + + */ +JsonValue JsonArray::at(int i) const +{ + if (!a || i < 0 || i >= (int)a->length) + return JsonValue(JsonValue::Undefined); + + return JsonValue(d, a, a->at(i)); +} + +/*! + Returns the first value stored in the array. + + Same as \c at(0). + + \sa at() + */ +JsonValue JsonArray::first() const +{ + return at(0); +} + +/*! + Returns the last value stored in the array. + + Same as \c{at(size() - 1)}. + + \sa at() + */ +JsonValue JsonArray::last() const +{ + return at(a ? (a->length - 1) : 0); +} + +/*! + Inserts \a value at the beginning of the array. + + This is the same as \c{insert(0, value)} and will prepend \a value to the array. + + \sa append(), insert() + */ +void JsonArray::prepend(const JsonValue &value) +{ + insert(0, value); +} + +/*! + Inserts \a value at the end of the array. + + \sa prepend(), insert() + */ +void JsonArray::append(const JsonValue &value) +{ + insert(a ? (int)a->length : 0, value); +} + +/*! + Removes the value at index position \a i. \a i must be a valid + index position in the array (i.e., \c{0 <= i < size()}). + + \sa insert(), replace() + */ +void JsonArray::removeAt(int i) +{ + if (!a || i < 0 || i >= (int)a->length) + return; + + detach(); + a->removeItems(i, 1); + ++d->compactionCounter; + if (d->compactionCounter > 32u && d->compactionCounter >= unsigned(a->length) / 2u) + compact(); +} + +/*! \fn void JsonArray::removeFirst() + + Removes the first item in the array. Calling this function is + equivalent to calling \c{removeAt(0)}. The array must not be empty. If + the array can be empty, call isEmpty() before calling this + function. + + \sa removeAt(), removeLast() +*/ + +/*! \fn void JsonArray::removeLast() + + Removes the last item in the array. Calling this function is + equivalent to calling \c{removeAt(size() - 1)}. The array must not be + empty. If the array can be empty, call isEmpty() before calling + this function. + + \sa removeAt(), removeFirst() +*/ + +/*! + Removes the item at index position \a i and returns it. \a i must + be a valid index position in the array (i.e., \c{0 <= i < size()}). + + If you don't use the return value, removeAt() is more efficient. + + \sa removeAt() + */ +JsonValue JsonArray::takeAt(int i) +{ + if (!a || i < 0 || i >= (int)a->length) + return JsonValue(JsonValue::Undefined); + + JsonValue v(d, a, a->at(i)); + removeAt(i); // detaches + return v; +} + +/*! + Inserts \a value at index position \a i in the array. If \a i + is \c 0, the value is prepended to the array. If \a i is size(), the + value is appended to the array. + + \sa append(), prepend(), replace(), removeAt() + */ +void JsonArray::insert(int i, const JsonValue &value) +{ + // assert (i >= 0 && i <= (a ? (int)a->length : 0)); + JsonValue val = value; + + bool compressed; + int valueSize = Internal::Value::requiredStorage(val, &compressed); + + detach(valueSize + sizeof(Internal::Value)); + + if (!a->length) + a->tableOffset = sizeof(Internal::Array); + + int valueOffset = a->reserveSpace(valueSize, i, 1, false); + if (!valueOffset) + return; + + Internal::Value &v = (*a)[i]; + v.type = (val.t == JsonValue::Undefined ? JsonValue::Null : val.t); + v.intValue = compressed; + v.value = Internal::Value::valueToStore(val, valueOffset); + if (valueSize) + Internal::Value::copyData(val, (char *)a + valueOffset, compressed); +} + +/*! + \fn JsonArray::iterator JsonArray::insert(iterator before, const JsonValue &value) + + Inserts \a value before the position pointed to by \a before, and returns an iterator + pointing to the newly inserted item. + + \sa erase(), insert() +*/ + +/*! + \fn JsonArray::iterator JsonArray::erase(iterator it) + + Removes the item pointed to by \a it, and returns an iterator pointing to the + next item. + + \sa removeAt() +*/ + +/*! + Replaces the item at index position \a i with \a value. \a i must + be a valid index position in the array (i.e., \c{0 <= i < size()}). + + \sa operator[](), removeAt() + */ +void JsonArray::replace(int i, const JsonValue &value) +{ + // assert (a && i >= 0 && i < (int)(a->length)); + JsonValue val = value; + + bool compressed; + int valueSize = Internal::Value::requiredStorage(val, &compressed); + + detach(valueSize); + + if (!a->length) + a->tableOffset = sizeof(Internal::Array); + + int valueOffset = a->reserveSpace(valueSize, i, 1, true); + if (!valueOffset) + return; + + Internal::Value &v = (*a)[i]; + v.type = (val.t == JsonValue::Undefined ? JsonValue::Null : val.t); + v.intValue = compressed; + v.value = Internal::Value::valueToStore(val, valueOffset); + if (valueSize) + Internal::Value::copyData(val, (char *)a + valueOffset, compressed); + + ++d->compactionCounter; + if (d->compactionCounter > 32u && d->compactionCounter >= unsigned(a->length) / 2u) + compact(); +} + +/*! + Returns \c true if the array contains an occurrence of \a value, otherwise \c false. + + \sa count() + */ +bool JsonArray::contains(const JsonValue &value) const +{ + for (int i = 0; i < size(); i++) { + if (at(i) == value) + return true; + } + return false; +} + +/*! + Returns the value at index position \a i as a modifiable reference. + \a i must be a valid index position in the array (i.e., \c{0 <= i < + size()}). + + The return value is of type JsonValueRef, a helper class for JsonArray + and JsonObject. When you get an object of type JsonValueRef, you can + use it as if it were a reference to a JsonValue. If you assign to it, + the assignment will apply to the character in the JsonArray of JsonObject + from which you got the reference. + + \sa at() + */ +JsonValueRef JsonArray::operator[](int i) +{ + // assert(a && i >= 0 && i < (int)a->length); + return JsonValueRef(this, i); +} + +/*! + \overload + + Same as at(). + */ +JsonValue JsonArray::operator[](int i) const +{ + return at(i); +} + +/*! + Returns \c true if this array is equal to \a other. + */ +bool JsonArray::operator==(const JsonArray &other) const +{ + if (a == other.a) + return true; + + if (!a) + return !other.a->length; + if (!other.a) + return !a->length; + if (a->length != other.a->length) + return false; + + for (int i = 0; i < (int)a->length; ++i) { + if (JsonValue(d, a, a->at(i)) != JsonValue(other.d, other.a, other.a->at(i))) + return false; + } + return true; +} + +/*! + Returns \c true if this array is not equal to \a other. + */ +bool JsonArray::operator!=(const JsonArray &other) const +{ + return !(*this == other); +} + +/*! \fn JsonArray::iterator JsonArray::begin() + + Returns an \l{STL-style iterators}{STL-style iterator} pointing to the first item in + the array. + + \sa constBegin(), end() +*/ + +/*! \fn JsonArray::const_iterator JsonArray::begin() const + + \overload +*/ + +/*! \fn JsonArray::const_iterator JsonArray::constBegin() const + + Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first item + in the array. + + \sa begin(), constEnd() +*/ + +/*! \fn JsonArray::iterator JsonArray::end() + + Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item + after the last item in the array. + + \sa begin(), constEnd() +*/ + +/*! \fn const_iterator JsonArray::end() const + + \overload +*/ + +/*! \fn JsonArray::const_iterator JsonArray::constEnd() const + + Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary + item after the last item in the array. + + \sa constBegin(), end() +*/ + +/*! \fn void JsonArray::push_back(const JsonValue &value) + + This function is provided for STL compatibility. It is equivalent + to \l{JsonArray::append()}{append(value)} and will append \a value to the array. +*/ + +/*! \fn void JsonArray::push_front(const JsonValue &value) + + This function is provided for STL compatibility. It is equivalent + to \l{JsonArray::prepend()}{prepend(value)} and will prepend \a value to the array. +*/ + +/*! \fn void JsonArray::pop_front() + + This function is provided for STL compatibility. It is equivalent + to removeFirst(). The array must not be empty. If the array can be + empty, call isEmpty() before calling this function. +*/ + +/*! \fn void JsonArray::pop_back() + + This function is provided for STL compatibility. It is equivalent + to removeLast(). The array must not be empty. If the array can be + empty, call isEmpty() before calling this function. +*/ + +/*! \fn bool JsonArray::empty() const + + This function is provided for STL compatibility. It is equivalent + to isEmpty() and returns \c true if the array is empty. +*/ + +/*! \class JsonArray::iterator + \inmodule QtCore + \brief The JsonArray::iterator class provides an STL-style non-const iterator for JsonArray. + + JsonArray::iterator allows you to iterate over a JsonArray + and to modify the array item associated with the + iterator. If you want to iterate over a const JsonArray, use + JsonArray::const_iterator instead. It is generally a good practice to + use JsonArray::const_iterator on a non-const JsonArray as well, unless + you need to change the JsonArray through the iterator. Const + iterators are slightly faster and improves code readability. + + The default JsonArray::iterator constructor creates an uninitialized + iterator. You must initialize it using a JsonArray function like + JsonArray::begin(), JsonArray::end(), or JsonArray::insert() before you can + start iterating. + + Most JsonArray functions accept an integer index rather than an + iterator. For that reason, iterators are rarely useful in + connection with JsonArray. One place where STL-style iterators do + make sense is as arguments to \l{generic algorithms}. + + Multiple iterators can be used on the same array. However, be + aware that any non-const function call performed on the JsonArray + will render all existing iterators undefined. + + \sa JsonArray::const_iterator +*/ + +/*! \typedef JsonArray::iterator::iterator_category + + A synonym for \e {std::random_access_iterator_tag} indicating + this iterator is a random access iterator. +*/ + +/*! \typedef JsonArray::iterator::difference_type + + \internal +*/ + +/*! \typedef JsonArray::iterator::value_type + + \internal +*/ + +/*! \typedef JsonArray::iterator::reference + + \internal +*/ + +/*! \typedef JsonArray::iterator::pointer + + \internal +*/ + +/*! \fn JsonArray::iterator::iterator() + + Constructs an uninitialized iterator. + + Functions like operator*() and operator++() should not be called + on an uninitialized iterator. Use operator=() to assign a value + to it before using it. + + \sa JsonArray::begin(), JsonArray::end() +*/ + +/*! \fn JsonArray::iterator::iterator(JsonArray *array, int index) + \internal +*/ + +/*! \fn JsonValueRef JsonArray::iterator::operator*() const + + + Returns a modifiable reference to the current item. + + You can change the value of an item by using operator*() on the + left side of an assignment. + + The return value is of type JsonValueRef, a helper class for JsonArray + and JsonObject. When you get an object of type JsonValueRef, you can + use it as if it were a reference to a JsonValue. If you assign to it, + the assignment will apply to the character in the JsonArray of JsonObject + from which you got the reference. +*/ + +/*! \fn JsonValueRef *JsonArray::iterator::operator->() const + + Returns a pointer to a modifiable reference to the current item. +*/ + +/*! \fn JsonValueRef JsonArray::iterator::operator[](int j) const + + Returns a modifiable reference to the item at offset \a j from the + item pointed to by this iterator (the item at position \c{*this + j}). + + This function is provided to make JsonArray iterators behave like C++ + pointers. + + The return value is of type JsonValueRef, a helper class for JsonArray + and JsonObject. When you get an object of type JsonValueRef, you can + use it as if it were a reference to a JsonValue. If you assign to it, + the assignment will apply to the character in the JsonArray of JsonObject + from which you got the reference. + + \sa operator+() +*/ + +/*! + \fn bool JsonArray::iterator::operator==(const iterator &other) const + \fn bool JsonArray::iterator::operator==(const const_iterator &other) const + + Returns \c true if \a other points to the same item as this + iterator; otherwise returns \c false. + + \sa operator!=() +*/ + +/*! + \fn bool JsonArray::iterator::operator!=(const iterator &other) const + \fn bool JsonArray::iterator::operator!=(const const_iterator &other) const + + Returns \c true if \a other points to a different item than this + iterator; otherwise returns \c false. + + \sa operator==() +*/ + +/*! + \fn bool JsonArray::iterator::operator<(const iterator& other) const + \fn bool JsonArray::iterator::operator<(const const_iterator& other) const + + Returns \c true if the item pointed to by this iterator is less than + the item pointed to by the \a other iterator. +*/ + +/*! + \fn bool JsonArray::iterator::operator<=(const iterator& other) const + \fn bool JsonArray::iterator::operator<=(const const_iterator& other) const + + Returns \c true if the item pointed to by this iterator is less than + or equal to the item pointed to by the \a other iterator. +*/ + +/*! + \fn bool JsonArray::iterator::operator>(const iterator& other) const + \fn bool JsonArray::iterator::operator>(const const_iterator& other) const + + Returns \c true if the item pointed to by this iterator is greater + than the item pointed to by the \a other iterator. +*/ + +/*! + \fn bool JsonArray::iterator::operator>=(const iterator& other) const + \fn bool JsonArray::iterator::operator>=(const const_iterator& other) const + + Returns \c true if the item pointed to by this iterator is greater + than or equal to the item pointed to by the \a other iterator. +*/ + +/*! \fn JsonArray::iterator &JsonArray::iterator::operator++() + + The prefix ++ operator, \c{++it}, advances the iterator to the + next item in the array and returns an iterator to the new current + item. + + Calling this function on JsonArray::end() leads to undefined results. + + \sa operator--() +*/ + +/*! \fn JsonArray::iterator JsonArray::iterator::operator++(int) + + \overload + + The postfix ++ operator, \c{it++}, advances the iterator to the + next item in the array and returns an iterator to the previously + current item. +*/ + +/*! \fn JsonArray::iterator &JsonArray::iterator::operator--() + + The prefix -- operator, \c{--it}, makes the preceding item + current and returns an iterator to the new current item. + + Calling this function on JsonArray::begin() leads to undefined results. + + \sa operator++() +*/ + +/*! \fn JsonArray::iterator JsonArray::iterator::operator--(int) + + \overload + + The postfix -- operator, \c{it--}, makes the preceding item + current and returns an iterator to the previously current item. +*/ + +/*! \fn JsonArray::iterator &JsonArray::iterator::operator+=(int j) + + Advances the iterator by \a j items. If \a j is negative, the + iterator goes backward. + + \sa operator-=(), operator+() +*/ + +/*! \fn JsonArray::iterator &JsonArray::iterator::operator-=(int j) + + Makes the iterator go back by \a j items. If \a j is negative, + the iterator goes forward. + + \sa operator+=(), operator-() +*/ + +/*! \fn JsonArray::iterator JsonArray::iterator::operator+(int j) const + + Returns an iterator to the item at \a j positions forward from + this iterator. If \a j is negative, the iterator goes backward. + + \sa operator-(), operator+=() +*/ + +/*! \fn JsonArray::iterator JsonArray::iterator::operator-(int j) const + + Returns an iterator to the item at \a j positions backward from + this iterator. If \a j is negative, the iterator goes forward. + + \sa operator+(), operator-=() +*/ + +/*! \fn int JsonArray::iterator::operator-(iterator other) const + + Returns the number of items between the item pointed to by \a + other and the item pointed to by this iterator. +*/ + +/*! \class JsonArray::const_iterator + \inmodule QtCore + \brief The JsonArray::const_iterator class provides an STL-style const iterator for JsonArray. + + JsonArray::const_iterator allows you to iterate over a + JsonArray. If you want to modify the JsonArray as + you iterate over it, use JsonArray::iterator instead. It is generally a + good practice to use JsonArray::const_iterator on a non-const JsonArray + as well, unless you need to change the JsonArray through the + iterator. Const iterators are slightly faster and improves + code readability. + + The default JsonArray::const_iterator constructor creates an + uninitialized iterator. You must initialize it using a JsonArray + function like JsonArray::constBegin(), JsonArray::constEnd(), or + JsonArray::insert() before you can start iterating. + + Most JsonArray functions accept an integer index rather than an + iterator. For that reason, iterators are rarely useful in + connection with JsonArray. One place where STL-style iterators do + make sense is as arguments to \l{generic algorithms}. + + Multiple iterators can be used on the same array. However, be + aware that any non-const function call performed on the JsonArray + will render all existing iterators undefined. + + \sa JsonArray::iterator +*/ + +/*! \fn JsonArray::const_iterator::const_iterator() + + Constructs an uninitialized iterator. + + Functions like operator*() and operator++() should not be called + on an uninitialized iterator. Use operator=() to assign a value + to it before using it. + + \sa JsonArray::constBegin(), JsonArray::constEnd() +*/ + +/*! \fn JsonArray::const_iterator::const_iterator(const JsonArray *array, int index) + \internal +*/ + +/*! \typedef JsonArray::const_iterator::iterator_category + + A synonym for \e {std::random_access_iterator_tag} indicating + this iterator is a random access iterator. +*/ + +/*! \typedef JsonArray::const_iterator::difference_type + + \internal +*/ + +/*! \typedef JsonArray::const_iterator::value_type + + \internal +*/ + +/*! \typedef JsonArray::const_iterator::reference + + \internal +*/ + +/*! \typedef JsonArray::const_iterator::pointer + + \internal +*/ + +/*! \fn JsonArray::const_iterator::const_iterator(const const_iterator &other) + + Constructs a copy of \a other. +*/ + +/*! \fn JsonArray::const_iterator::const_iterator(const iterator &other) + + Constructs a copy of \a other. +*/ + +/*! \fn JsonValue JsonArray::const_iterator::operator*() const + + Returns the current item. +*/ + +/*! \fn JsonValue *JsonArray::const_iterator::operator->() const + + Returns a pointer to the current item. +*/ + +/*! \fn JsonValue JsonArray::const_iterator::operator[](int j) const + + Returns the item at offset \a j from the item pointed to by this iterator (the item at + position \c{*this + j}). + + This function is provided to make JsonArray iterators behave like C++ + pointers. + + \sa operator+() +*/ + +/*! \fn bool JsonArray::const_iterator::operator==(const const_iterator &other) const + + Returns \c true if \a other points to the same item as this + iterator; otherwise returns \c false. + + \sa operator!=() +*/ + +/*! \fn bool JsonArray::const_iterator::operator!=(const const_iterator &other) const + + Returns \c true if \a other points to a different item than this + iterator; otherwise returns \c false. + + \sa operator==() +*/ + +/*! + \fn bool JsonArray::const_iterator::operator<(const const_iterator& other) const + + Returns \c true if the item pointed to by this iterator is less than + the item pointed to by the \a other iterator. +*/ + +/*! + \fn bool JsonArray::const_iterator::operator<=(const const_iterator& other) const + + Returns \c true if the item pointed to by this iterator is less than + or equal to the item pointed to by the \a other iterator. +*/ + +/*! + \fn bool JsonArray::const_iterator::operator>(const const_iterator& other) const + + Returns \c true if the item pointed to by this iterator is greater + than the item pointed to by the \a other iterator. +*/ + +/*! + \fn bool JsonArray::const_iterator::operator>=(const const_iterator& other) const + + Returns \c true if the item pointed to by this iterator is greater + than or equal to the item pointed to by the \a other iterator. +*/ + +/*! \fn JsonArray::const_iterator &JsonArray::const_iterator::operator++() + + The prefix ++ operator, \c{++it}, advances the iterator to the + next item in the array and returns an iterator to the new current + item. + + Calling this function on JsonArray::end() leads to undefined results. + + \sa operator--() +*/ + +/*! \fn JsonArray::const_iterator JsonArray::const_iterator::operator++(int) + + \overload + + The postfix ++ operator, \c{it++}, advances the iterator to the + next item in the array and returns an iterator to the previously + current item. +*/ + +/*! \fn JsonArray::const_iterator &JsonArray::const_iterator::operator--() + + The prefix -- operator, \c{--it}, makes the preceding item + current and returns an iterator to the new current item. + + Calling this function on JsonArray::begin() leads to undefined results. + + \sa operator++() +*/ + +/*! \fn JsonArray::const_iterator JsonArray::const_iterator::operator--(int) + + \overload + + The postfix -- operator, \c{it--}, makes the preceding item + current and returns an iterator to the previously current item. +*/ + +/*! \fn JsonArray::const_iterator &JsonArray::const_iterator::operator+=(int j) + + Advances the iterator by \a j items. If \a j is negative, the + iterator goes backward. + + \sa operator-=(), operator+() +*/ + +/*! \fn JsonArray::const_iterator &JsonArray::const_iterator::operator-=(int j) + + Makes the iterator go back by \a j items. If \a j is negative, + the iterator goes forward. + + \sa operator+=(), operator-() +*/ + +/*! \fn JsonArray::const_iterator JsonArray::const_iterator::operator+(int j) const + + Returns an iterator to the item at \a j positions forward from + this iterator. If \a j is negative, the iterator goes backward. + + \sa operator-(), operator+=() +*/ + +/*! \fn JsonArray::const_iterator JsonArray::const_iterator::operator-(int j) const + + Returns an iterator to the item at \a j positions backward from + this iterator. If \a j is negative, the iterator goes forward. + + \sa operator+(), operator-=() +*/ + +/*! \fn int JsonArray::const_iterator::operator-(const_iterator other) const + + Returns the number of items between the item pointed to by \a + other and the item pointed to by this iterator. +*/ + + +/*! + \internal + */ +void JsonArray::detach(uint32_t reserve) +{ + if (!d) { + d = new Internal::Data(reserve, JsonValue::Array); + a = static_cast(d->header->root()); + d->ref.ref(); + return; + } + if (reserve == 0 && d->ref.load() == 1) + return; + + Internal::Data *x = d->clone(a, reserve); + x->ref.ref(); + if (!d->ref.deref()) + delete d; + d = x; + a = static_cast(d->header->root()); +} + +/*! + \internal + */ +void JsonArray::compact() +{ + if (!d || !d->compactionCounter) + return; + + detach(); + d->compact(); + a = static_cast(d->header->root()); +} + +/*! + \class JsonObject + \inmodule QtCore + \ingroup json + \ingroup shared + \reentrant + \since 5.0 + + \brief The JsonObject class encapsulates a JSON object. + + A JSON object is a list of key value pairs, where the keys are unique strings + and the values are represented by a JsonValue. + + A JsonObject can be converted to and from a QVariantMap. You can query the + number of (key, value) pairs with size(), insert(), and remove() entries from it + and iterate over its content using the standard C++ iterator pattern. + + JsonObject is an implicitly shared class, and shares the data with the document + it has been created from as long as it is not being modified. + + You can convert the object to and from text based JSON through JsonDocument. + + \sa {JSON Support in Qt}, {JSON Save Game Example} +*/ + +/*! + \typedef JsonObject::Iterator + + Qt-style synonym for JsonObject::iterator. +*/ + +/*! + \typedef JsonObject::ConstIterator + + Qt-style synonym for JsonObject::const_iterator. +*/ + +/*! + \typedef JsonObject::key_type + + Typedef for QString. Provided for STL compatibility. +*/ + +/*! + \typedef JsonObject::mapped_type + + Typedef for JsonValue. Provided for STL compatibility. +*/ + +/*! + \typedef JsonObject::size_type + + Typedef for int. Provided for STL compatibility. +*/ + + +/*! + Constructs an empty JSON object. + + \sa isEmpty() + */ +JsonObject::JsonObject() + : d(0), o(0) +{ +} + +JsonObject::JsonObject(std::initializer_list > args) + : d(0), o(0) +{ + for (auto i = args.begin(); i != args.end(); ++i) + insert(i->first, i->second); +} + +/*! + \fn JsonObject::JsonObject(std::initializer_list > args) + \since 5.4 + Constructs a JsonObject instance initialized from \a args initialization list. + For example: + \code + JsonObject object + { + {"property1", 1}, + {"property2", 2} + }; + \endcode +*/ + +/*! + \internal + */ +JsonObject::JsonObject(Internal::Data *data, Internal::Object *object) + : d(data), o(object) +{ + // assert(d); + // assert(o); + d->ref.ref(); +} + +/*! + This method replaces part of the JsonObject(std::initializer_list> args) body. + The constructor needs to be inline, but we do not want to leak implementation details + of this class. + \note this method is called for an uninitialized object + \internal + */ + +/*! + Destroys the object. + */ +JsonObject::~JsonObject() +{ + if (d && !d->ref.deref()) + delete d; +} + +/*! + Creates a copy of \a other. + + Since JsonObject is implicitly shared, the copy is shallow + as long as the object does not get modified. + */ +JsonObject::JsonObject(const JsonObject &other) +{ + d = other.d; + o = other.o; + if (d) + d->ref.ref(); +} + +/*! + Assigns \a other to this object. + */ +JsonObject &JsonObject::operator=(const JsonObject &other) +{ + if (d != other.d) { + if (d && !d->ref.deref()) + delete d; + d = other.d; + if (d) + d->ref.ref(); + } + o = other.o; + + return *this; +} + +/*! + Returns a list of all keys in this object. + + The list is sorted lexographically. + */ +JsonObject::Keys JsonObject::keys() const +{ + Keys keys; + if (!d) + return keys; + + keys.reserve(o->length); + for (uint32_t i = 0; i < o->length; ++i) { + Internal::Entry *e = o->entryAt(i); + keys.push_back(e->key().data()); + } + + return keys; +} + +/*! + Returns the number of (key, value) pairs stored in the object. + */ +int JsonObject::size() const +{ + if (!d) + return 0; + + return o->length; +} + +/*! + Returns \c true if the object is empty. This is the same as size() == 0. + + \sa size() + */ +bool JsonObject::isEmpty() const +{ + if (!d) + return true; + + return !o->length; +} + +/*! + Returns a JsonValue representing the value for the key \a key. + + The returned JsonValue is JsonValue::Undefined if the key does not exist. + + \sa JsonValue, JsonValue::isUndefined() + */ +JsonValue JsonObject::value(const std::string &key) const +{ + if (!d) + return JsonValue(JsonValue::Undefined); + + bool keyExists; + int i = o->indexOf(key, &keyExists); + if (!keyExists) + return JsonValue(JsonValue::Undefined); + return JsonValue(d, o, o->entryAt(i)->value); +} + +/*! + Returns a JsonValue representing the value for the key \a key. + + This does the same as value(). + + The returned JsonValue is JsonValue::Undefined if the key does not exist. + + \sa value(), JsonValue, JsonValue::isUndefined() + */ +JsonValue JsonObject::operator[](const std::string &key) const +{ + return value(key); +} + +/*! + Returns a reference to the value for \a key. + + The return value is of type JsonValueRef, a helper class for JsonArray + and JsonObject. When you get an object of type JsonValueRef, you can + use it as if it were a reference to a JsonValue. If you assign to it, + the assignment will apply to the element in the JsonArray or JsonObject + from which you got the reference. + + \sa value() + */ +JsonValueRef JsonObject::operator[](const std::string &key) +{ + // ### somewhat inefficient, as we lookup the key twice if it doesn't yet exist + bool keyExists = false; + int index = o ? o->indexOf(key, &keyExists) : -1; + if (!keyExists) { + iterator i = insert(key, JsonValue()); + index = i.i; + } + return JsonValueRef(this, index); +} + +/*! + Inserts a new item with the key \a key and a value of \a value. + + If there is already an item with the key \a key, then that item's value + is replaced with \a value. + + Returns an iterator pointing to the inserted item. + + If the value is JsonValue::Undefined, it will cause the key to get removed + from the object. The returned iterator will then point to end(). + + \sa remove(), take(), JsonObject::iterator, end() + */ +JsonObject::iterator JsonObject::insert(const std::string &key, const JsonValue &value) +{ + if (value.t == JsonValue::Undefined) { + remove(key); + return end(); + } + JsonValue val = value; + + bool isIntValue; + int valueSize = Internal::Value::requiredStorage(val, &isIntValue); + + int valueOffset = sizeof(Internal::Entry) + Internal::qStringSize(key); + int requiredSize = valueOffset + valueSize; + + detach(requiredSize + sizeof(Internal::offset)); // offset for the new index entry + + if (!o->length) + o->tableOffset = sizeof(Internal::Object); + + bool keyExists = false; + int pos = o->indexOf(key, &keyExists); + if (keyExists) + ++d->compactionCounter; + + uint32_t off = o->reserveSpace(requiredSize, pos, 1, keyExists); + if (!off) + return end(); + + Internal::Entry *e = o->entryAt(pos); + e->value.type = val.t; + e->value.intValue = isIntValue; + e->value.value = Internal::Value::valueToStore(val, (char *)e - (char *)o + valueOffset); + Internal::copyString((char *)(e + 1), key); + if (valueSize) + Internal::Value::copyData(val, (char *)e + valueOffset, isIntValue); + + if (d->compactionCounter > 32u && d->compactionCounter >= unsigned(o->length) / 2u) + compact(); + + return iterator(this, pos); +} + +/*! + Removes \a key from the object. + + \sa insert(), take() + */ +void JsonObject::remove(const std::string &key) +{ + if (!d) + return; + + bool keyExists; + int index = o->indexOf(key, &keyExists); + if (!keyExists) + return; + + detach(); + o->removeItems(index, 1); + ++d->compactionCounter; + if (d->compactionCounter > 32u && d->compactionCounter >= unsigned(o->length) / 2u) + compact(); +} + +/*! + Removes \a key from the object. + + Returns a JsonValue containing the value referenced by \a key. + If \a key was not contained in the object, the returned JsonValue + is JsonValue::Undefined. + + \sa insert(), remove(), JsonValue + */ +JsonValue JsonObject::take(const std::string &key) +{ + if (!o) + return JsonValue(JsonValue::Undefined); + + bool keyExists; + int index = o->indexOf(key, &keyExists); + if (!keyExists) + return JsonValue(JsonValue::Undefined); + + JsonValue v(d, o, o->entryAt(index)->value); + detach(); + o->removeItems(index, 1); + ++d->compactionCounter; + if (d->compactionCounter > 32u && d->compactionCounter >= unsigned(o->length) / 2u) + compact(); + + return v; +} + +/*! + Returns \c true if the object contains key \a key. + + \sa insert(), remove(), take() + */ +bool JsonObject::contains(const std::string &key) const +{ + if (!o) + return false; + + bool keyExists; + o->indexOf(key, &keyExists); + return keyExists; +} + +/*! + Returns \c true if \a other is equal to this object. + */ +bool JsonObject::operator==(const JsonObject &other) const +{ + if (o == other.o) + return true; + + if (!o) + return !other.o->length; + if (!other.o) + return !o->length; + if (o->length != other.o->length) + return false; + + for (uint32_t i = 0; i < o->length; ++i) { + Internal::Entry *e = o->entryAt(i); + JsonValue v(d, o, e->value); + if (other.value(e->key()) != v) + return false; + } + + return true; +} + +/*! + Returns \c true if \a other is not equal to this object. + */ +bool JsonObject::operator!=(const JsonObject &other) const +{ + return !(*this == other); +} + +/*! + Removes the (key, value) pair pointed to by the iterator \a it + from the map, and returns an iterator to the next item in the + map. + + \sa remove() + */ +JsonObject::iterator JsonObject::erase(JsonObject::iterator it) +{ + // assert(d && d->ref.load() == 1); + if (it.o != this || it.i < 0 || it.i >= (int)o->length) + return iterator(this, o->length); + + int index = it.i; + + o->removeItems(index, 1); + ++d->compactionCounter; + if (d->compactionCounter > 32u && d->compactionCounter >= unsigned(o->length) / 2u) + compact(); + + // iterator hasn't changed + return it; +} + +/*! + Returns an iterator pointing to the item with key \a key in the + map. + + If the map contains no item with key \a key, the function + returns end(). + */ +JsonObject::iterator JsonObject::find(const std::string &key) +{ + bool keyExists = false; + int index = o ? o->indexOf(key, &keyExists) : 0; + if (!keyExists) + return end(); + detach(); + return iterator(this, index); +} + +/*! \fn JsonObject::const_iterator JsonObject::find(const QString &key) const + + \overload +*/ + +/*! + Returns a const iterator pointing to the item with key \a key in the + map. + + If the map contains no item with key \a key, the function + returns constEnd(). + */ +JsonObject::const_iterator JsonObject::constFind(const std::string &key) const +{ + bool keyExists = false; + int index = o ? o->indexOf(key, &keyExists) : 0; + if (!keyExists) + return end(); + return const_iterator(this, index); +} + +/*! \fn int JsonObject::count() const + + \overload + + Same as size(). +*/ + +/*! \fn int JsonObject::length() const + + \overload + + Same as size(). +*/ + +/*! \fn JsonObject::iterator JsonObject::begin() + + Returns an \l{STL-style iterators}{STL-style iterator} pointing to the first item in + the object. + + \sa constBegin(), end() +*/ + +/*! \fn JsonObject::const_iterator JsonObject::begin() const + + \overload +*/ + +/*! \fn JsonObject::const_iterator JsonObject::constBegin() const + + Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first item + in the object. + + \sa begin(), constEnd() +*/ + +/*! \fn JsonObject::iterator JsonObject::end() + + Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item + after the last item in the object. + + \sa begin(), constEnd() +*/ + +/*! \fn JsonObject::const_iterator JsonObject::end() const + + \overload +*/ + +/*! \fn JsonObject::const_iterator JsonObject::constEnd() const + + Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary + item after the last item in the object. + + \sa constBegin(), end() +*/ + +/*! + \fn bool JsonObject::empty() const + + This function is provided for STL compatibility. It is equivalent + to isEmpty(), returning \c true if the object is empty; otherwise + returning \c false. +*/ + +/*! \class JsonObject::iterator + \inmodule QtCore + \ingroup json + \reentrant + \since 5.0 + + \brief The JsonObject::iterator class provides an STL-style non-const iterator for JsonObject. + + JsonObject::iterator allows you to iterate over a JsonObject + and to modify the value (but not the key) stored under + a particular key. If you want to iterate over a const JsonObject, you + should use JsonObject::const_iterator. It is generally good practice to + use JsonObject::const_iterator on a non-const JsonObject as well, unless you + need to change the JsonObject through the iterator. Const iterators are + slightly faster, and improve code readability. + + The default JsonObject::iterator constructor creates an uninitialized + iterator. You must initialize it using a JsonObject function like + JsonObject::begin(), JsonObject::end(), or JsonObject::find() before you can + start iterating. + + Multiple iterators can be used on the same object. Existing iterators will however + become dangling once the object gets modified. + + \sa JsonObject::const_iterator, {JSON Support in Qt}, {JSON Save Game Example} +*/ + +/*! \typedef JsonObject::iterator::difference_type + + \internal +*/ + +/*! \typedef JsonObject::iterator::iterator_category + + A synonym for \e {std::bidirectional_iterator_tag} indicating + this iterator is a bidirectional iterator. +*/ + +/*! \typedef JsonObject::iterator::reference + + \internal +*/ + +/*! \typedef JsonObject::iterator::value_type + + \internal +*/ + +/*! \fn JsonObject::iterator::iterator() + + Constructs an uninitialized iterator. + + Functions like key(), value(), and operator++() must not be + called on an uninitialized iterator. Use operator=() to assign a + value to it before using it. + + \sa JsonObject::begin(), JsonObject::end() +*/ + +/*! \fn JsonObject::iterator::iterator(JsonObject *obj, int index) + \internal +*/ + +/*! \fn QString JsonObject::iterator::key() const + + Returns the current item's key. + + There is no direct way of changing an item's key through an + iterator, although it can be done by calling JsonObject::erase() + followed by JsonObject::insert(). + + \sa value() +*/ + +/*! \fn JsonValueRef JsonObject::iterator::value() const + + Returns a modifiable reference to the current item's value. + + You can change the value of an item by using value() on + the left side of an assignment. + + The return value is of type JsonValueRef, a helper class for JsonArray + and JsonObject. When you get an object of type JsonValueRef, you can + use it as if it were a reference to a JsonValue. If you assign to it, + the assignment will apply to the element in the JsonArray or JsonObject + from which you got the reference. + + \sa key(), operator*() +*/ + +/*! \fn JsonValueRef JsonObject::iterator::operator*() const + + Returns a modifiable reference to the current item's value. + + Same as value(). + + The return value is of type JsonValueRef, a helper class for JsonArray + and JsonObject. When you get an object of type JsonValueRef, you can + use it as if it were a reference to a JsonValue. If you assign to it, + the assignment will apply to the element in the JsonArray or JsonObject + from which you got the reference. + + \sa key() +*/ + +/*! \fn JsonValueRef *JsonObject::iterator::operator->() const + + Returns a pointer to a modifiable reference to the current item. +*/ + +/*! + \fn bool JsonObject::iterator::operator==(const iterator &other) const + \fn bool JsonObject::iterator::operator==(const const_iterator &other) const + + Returns \c true if \a other points to the same item as this + iterator; otherwise returns \c false. + + \sa operator!=() +*/ + +/*! + \fn bool JsonObject::iterator::operator!=(const iterator &other) const + \fn bool JsonObject::iterator::operator!=(const const_iterator &other) const + + Returns \c true if \a other points to a different item than this + iterator; otherwise returns \c false. + + \sa operator==() +*/ + +/*! \fn JsonObject::iterator JsonObject::iterator::operator++() + + The prefix ++ operator, \c{++i}, advances the iterator to the + next item in the object and returns an iterator to the new current + item. + + Calling this function on JsonObject::end() leads to undefined results. + + \sa operator--() +*/ + +/*! \fn JsonObject::iterator JsonObject::iterator::operator++(int) + + \overload + + The postfix ++ operator, \c{i++}, advances the iterator to the + next item in the object and returns an iterator to the previously + current item. +*/ + +/*! \fn JsonObject::iterator JsonObject::iterator::operator--() + + The prefix -- operator, \c{--i}, makes the preceding item + current and returns an iterator pointing to the new current item. + + Calling this function on JsonObject::begin() leads to undefined + results. + + \sa operator++() +*/ + +/*! \fn JsonObject::iterator JsonObject::iterator::operator--(int) + + \overload + + The postfix -- operator, \c{i--}, makes the preceding item + current and returns an iterator pointing to the previously + current item. +*/ + +/*! \fn JsonObject::iterator JsonObject::iterator::operator+(int j) const + + Returns an iterator to the item at \a j positions forward from + this iterator. If \a j is negative, the iterator goes backward. + + \sa operator-() + +*/ + +/*! \fn JsonObject::iterator JsonObject::iterator::operator-(int j) const + + Returns an iterator to the item at \a j positions backward from + this iterator. If \a j is negative, the iterator goes forward. + + \sa operator+() +*/ + +/*! \fn JsonObject::iterator &JsonObject::iterator::operator+=(int j) + + Advances the iterator by \a j items. If \a j is negative, the + iterator goes backward. + + \sa operator-=(), operator+() +*/ + +/*! \fn JsonObject::iterator &JsonObject::iterator::operator-=(int j) + + Makes the iterator go back by \a j items. If \a j is negative, + the iterator goes forward. + + \sa operator+=(), operator-() +*/ + +/*! + \class JsonObject::const_iterator + \inmodule QtCore + \ingroup json + \since 5.0 + \brief The JsonObject::const_iterator class provides an STL-style const iterator for JsonObject. + + JsonObject::const_iterator allows you to iterate over a JsonObject. + If you want to modify the JsonObject as you iterate + over it, you must use JsonObject::iterator instead. It is generally + good practice to use JsonObject::const_iterator on a non-const JsonObject as + well, unless you need to change the JsonObject through the iterator. + Const iterators are slightly faster and improve code + readability. + + The default JsonObject::const_iterator constructor creates an + uninitialized iterator. You must initialize it using a JsonObject + function like JsonObject::constBegin(), JsonObject::constEnd(), or + JsonObject::find() before you can start iterating. + + Multiple iterators can be used on the same object. Existing iterators + will however become dangling if the object gets modified. + + \sa JsonObject::iterator, {JSON Support in Qt}, {JSON Save Game Example} +*/ + +/*! \typedef JsonObject::const_iterator::difference_type + + \internal +*/ + +/*! \typedef JsonObject::const_iterator::iterator_category + + A synonym for \e {std::bidirectional_iterator_tag} indicating + this iterator is a bidirectional iterator. +*/ + +/*! \typedef JsonObject::const_iterator::reference + + \internal +*/ + +/*! \typedef JsonObject::const_iterator::value_type + + \internal +*/ + +/*! \fn JsonObject::const_iterator::const_iterator() + + Constructs an uninitialized iterator. + + Functions like key(), value(), and operator++() must not be + called on an uninitialized iterator. Use operator=() to assign a + value to it before using it. + + \sa JsonObject::constBegin(), JsonObject::constEnd() +*/ + +/*! \fn JsonObject::const_iterator::const_iterator(const JsonObject *obj, int index) + \internal +*/ + +/*! \fn JsonObject::const_iterator::const_iterator(const iterator &other) + + Constructs a copy of \a other. +*/ + +/*! \fn QString JsonObject::const_iterator::key() const + + Returns the current item's key. + + \sa value() +*/ + +/*! \fn JsonValue JsonObject::const_iterator::value() const + + Returns the current item's value. + + \sa key(), operator*() +*/ + +/*! \fn JsonValue JsonObject::const_iterator::operator*() const + + Returns the current item's value. + + Same as value(). + + \sa key() +*/ + +/*! \fn JsonValue *JsonObject::const_iterator::operator->() const + + Returns a pointer to the current item. +*/ + +/*! \fn bool JsonObject::const_iterator::operator==(const const_iterator &other) const + \fn bool JsonObject::const_iterator::operator==(const iterator &other) const + + Returns \c true if \a other points to the same item as this + iterator; otherwise returns \c false. + + \sa operator!=() +*/ + +/*! \fn bool JsonObject::const_iterator::operator!=(const const_iterator &other) const + \fn bool JsonObject::const_iterator::operator!=(const iterator &other) const + + Returns \c true if \a other points to a different item than this + iterator; otherwise returns \c false. + + \sa operator==() +*/ + +/*! \fn JsonObject::const_iterator JsonObject::const_iterator::operator++() + + The prefix ++ operator, \c{++i}, advances the iterator to the + next item in the object and returns an iterator to the new current + item. + + Calling this function on JsonObject::end() leads to undefined results. + + \sa operator--() +*/ + +/*! \fn JsonObject::const_iterator JsonObject::const_iterator::operator++(int) + + \overload + + The postfix ++ operator, \c{i++}, advances the iterator to the + next item in the object and returns an iterator to the previously + current item. +*/ + +/*! \fn JsonObject::const_iterator &JsonObject::const_iterator::operator--() + + The prefix -- operator, \c{--i}, makes the preceding item + current and returns an iterator pointing to the new current item. + + Calling this function on JsonObject::begin() leads to undefined + results. + + \sa operator++() +*/ + +/*! \fn JsonObject::const_iterator JsonObject::const_iterator::operator--(int) + + \overload + + The postfix -- operator, \c{i--}, makes the preceding item + current and returns an iterator pointing to the previously + current item. +*/ + +/*! \fn JsonObject::const_iterator JsonObject::const_iterator::operator+(int j) const + + Returns an iterator to the item at \a j positions forward from + this iterator. If \a j is negative, the iterator goes backward. + + This operation can be slow for large \a j values. + + \sa operator-() +*/ + +/*! \fn JsonObject::const_iterator JsonObject::const_iterator::operator-(int j) const + + Returns an iterator to the item at \a j positions backward from + this iterator. If \a j is negative, the iterator goes forward. + + This operation can be slow for large \a j values. + + \sa operator+() +*/ + +/*! \fn JsonObject::const_iterator &JsonObject::const_iterator::operator+=(int j) + + Advances the iterator by \a j items. If \a j is negative, the + iterator goes backward. + + This operation can be slow for large \a j values. + + \sa operator-=(), operator+() +*/ + +/*! \fn JsonObject::const_iterator &JsonObject::const_iterator::operator-=(int j) + + Makes the iterator go back by \a j items. If \a j is negative, + the iterator goes forward. + + This operation can be slow for large \a j values. + + \sa operator+=(), operator-() +*/ + + +/*! + \internal + */ +void JsonObject::detach(uint32_t reserve) +{ + if (!d) { + d = new Internal::Data(reserve, JsonValue::Object); + o = static_cast(d->header->root()); + d->ref.ref(); + return; + } + if (reserve == 0 && d->ref.load() == 1) + return; + + Internal::Data *x = d->clone(o, reserve); + x->ref.ref(); + if (!d->ref.deref()) + delete d; + d = x; + o = static_cast(d->header->root()); +} + +/*! + \internal + */ +void JsonObject::compact() +{ + if (!d || !d->compactionCounter) + return; + + detach(); + d->compact(); + o = static_cast(d->header->root()); +} + +/*! + \internal + */ +std::string JsonObject::keyAt(int i) const +{ + // assert(o && i >= 0 && i < (int)o->length); + + Internal::Entry *e = o->entryAt(i); + return e->key(); +} + +/*! + \internal + */ +JsonValue JsonObject::valueAt(int i) const +{ + if (!o || i < 0 || i >= (int)o->length) + return JsonValue(JsonValue::Undefined); + + Internal::Entry *e = o->entryAt(i); + return JsonValue(d, o, e->value); +} + +/*! + \internal + */ +void JsonObject::setValueAt(int i, const JsonValue &val) +{ + // assert(o && i >= 0 && i < (int)o->length); + + Internal::Entry *e = o->entryAt(i); + insert(e->key(), val); +} + + +/*! \class JsonDocument + \inmodule QtCore + \ingroup json + \ingroup shared + \reentrant + \since 5.0 + + \brief The JsonDocument class provides a way to read and write JSON documents. + + JsonDocument is a class that wraps a complete JSON document and can read and + write this document both from a UTF-8 encoded text based representation as well + as Qt's own binary format. + + A JSON document can be converted from its text-based representation to a JsonDocument + using JsonDocument::fromJson(). toJson() converts it back to text. The parser is very + fast and efficient and converts the JSON to the binary representation used by Qt. + + Validity of the parsed document can be queried with !isNull() + + A document can be queried as to whether it contains an array or an object using isArray() + and isObject(). The array or object contained in the document can be retrieved using + array() or object() and then read or manipulated. + + A document can also be created from a stored binary representation using fromBinaryData() or + fromRawData(). + + \sa {JSON Support in Qt}, {JSON Save Game Example} +*/ + +/*! + * Constructs an empty and invalid document. + */ +JsonDocument::JsonDocument() + : d(0) +{ +} + +/*! + * Creates a JsonDocument from \a object. + */ +JsonDocument::JsonDocument(const JsonObject &object) + : d(0) +{ + setObject(object); +} + +/*! + * Constructs a JsonDocument from \a array. + */ +JsonDocument::JsonDocument(const JsonArray &array) + : d(0) +{ + setArray(array); +} + +/*! + \internal + */ +JsonDocument::JsonDocument(Internal::Data *data) + : d(data) +{ + // assert(d); + d->ref.ref(); +} + +/*! + Deletes the document. + + Binary data set with fromRawData is not freed. + */ +JsonDocument::~JsonDocument() +{ + if (d && !d->ref.deref()) + delete d; +} + +/*! + * Creates a copy of the \a other document. + */ +JsonDocument::JsonDocument(const JsonDocument &other) +{ + d = other.d; + if (d) + d->ref.ref(); +} + +/*! + * Assigns the \a other document to this JsonDocument. + * Returns a reference to this object. + */ +JsonDocument &JsonDocument::operator=(const JsonDocument &other) +{ + if (d != other.d) { + if (d && !d->ref.deref()) + delete d; + d = other.d; + if (d) + d->ref.ref(); + } + + return *this; +} + +/*! \enum JsonDocument::DataValidation + + This value is used to tell JsonDocument whether to validate the binary data + when converting to a JsonDocument using fromBinaryData() or fromRawData(). + + \value Validate Validate the data before using it. This is the default. + \value BypassValidation Bypasses data validation. Only use if you received the + data from a trusted place and know it's valid, as using of invalid data can crash + the application. + */ + +/*! + Creates a JsonDocument that uses the first \a size bytes from + \a data. It assumes \a data contains a binary encoded JSON document. + The created document does not take ownership of \a data and the caller + has to guarantee that \a data will not be deleted or modified as long as + any JsonDocument, JsonObject or JsonArray still references the data. + + \a data has to be aligned to a 4 byte boundary. + + \a validation decides whether the data is checked for validity before being used. + By default the data is validated. If the \a data is not valid, the method returns + a null document. + + Returns a JsonDocument representing the data. + + \sa rawData(), fromBinaryData(), isNull(), DataValidation + */ +JsonDocument JsonDocument::fromRawData(const char *data, int size, DataValidation validation) +{ + if (std::uintptr_t(data) & 3) { + std::cerr <<"JsonDocument::fromRawData: data has to have 4 byte alignment\n"; + return JsonDocument(); + } + + Internal::Data *d = new Internal::Data((char *)data, size); + d->ownsData = false; + + if (validation != BypassValidation && !d->valid()) { + delete d; + return JsonDocument(); + } + + return JsonDocument(d); +} + +/*! + Returns the raw binary representation of the data + \a size will contain the size of the returned data. + + This method is useful to e.g. stream the JSON document + in it's binary form to a file. + */ +const char *JsonDocument::rawData(int *size) const +{ + if (!d) { + *size = 0; + return 0; + } + *size = d->alloc; + return d->rawData; +} + +/*! + Creates a JsonDocument from \a data. + + \a validation decides whether the data is checked for validity before being used. + By default the data is validated. If the \a data is not valid, the method returns + a null document. + + \sa toBinaryData(), fromRawData(), isNull(), DataValidation + */ +JsonDocument JsonDocument::fromBinaryData(const std::string &data, DataValidation validation) +{ + if (data.size() < (int)(sizeof(Internal::Header) + sizeof(Internal::Base))) + return JsonDocument(); + + Internal::Header h; + memcpy(&h, data.data(), sizeof(Internal::Header)); + Internal::Base root; + memcpy(&root, data.data() + sizeof(Internal::Header), sizeof(Internal::Base)); + + // do basic checks here, so we don't try to allocate more memory than we can. + if (h.tag != JsonDocument::BinaryFormatTag || h.version != 1u || + sizeof(Internal::Header) + root.size > (uint32_t)data.size()) + return JsonDocument(); + + const uint32_t size = sizeof(Internal::Header) + root.size; + char *raw = (char *)malloc(size); + if (!raw) + return JsonDocument(); + + memcpy(raw, data.data(), size); + Internal::Data *d = new Internal::Data(raw, size); + + if (validation != BypassValidation && !d->valid()) { + delete d; + return JsonDocument(); + } + + return JsonDocument(d); +} + +/*! + \enum JsonDocument::JsonFormat + + This value defines the format of the JSON byte array produced + when converting to a JsonDocument using toJson(). + + \value Indented Defines human readable output as follows: + \code + { + "Array": [ + true, + 999, + "string" + ], + "Key": "Value", + "null": null + } + \endcode + + \value Compact Defines a compact output as follows: + \code + {"Array":[true,999,"string"],"Key":"Value","null":null} + \endcode + */ + +/*! + Converts the JsonDocument to a UTF-8 encoded JSON document in the provided \a format. + + \sa fromJson(), JsonFormat + */ +#ifndef QT_JSON_READONLY +std::string JsonDocument::toJson(JsonFormat format) const +{ + std::string json; + + if (!d) + return json; + + if (d->header->root()->isArray()) + Internal::arrayToJson(static_cast(d->header->root()), json, 0, (format == Compact)); + else + Internal::objectToJson(static_cast(d->header->root()), json, 0, (format == Compact)); + + return json; +} +#endif + +/*! + Parses a UTF-8 encoded JSON document and creates a JsonDocument + from it. + + \a json contains the json document to be parsed. + + The optional \a error variable can be used to pass in a JsonParseError data + structure that will contain information about possible errors encountered during + parsing. + + \sa toJson(), JsonParseError + */ +JsonDocument JsonDocument::fromJson(const std::string &json, JsonParseError *error) +{ + Internal::Parser parser(json.data(), json.length()); + return parser.parse(error); +} + +/*! + Returns \c true if the document doesn't contain any data. + */ +bool JsonDocument::isEmpty() const +{ + if (!d) + return true; + + return false; +} + +/*! + Returns a binary representation of the document. + + The binary representation is also the native format used internally in Qt, + and is very efficient and fast to convert to and from. + + The binary format can be stored on disk and interchanged with other applications + or computers. fromBinaryData() can be used to convert it back into a + JSON document. + + \sa fromBinaryData() + */ +std::string JsonDocument::toBinaryData() const +{ + if (!d || !d->rawData) + return std::string(); + + return std::string(d->rawData, d->header->root()->size + sizeof(Internal::Header)); +} + +/*! + Returns \c true if the document contains an array. + + \sa array(), isObject() + */ +bool JsonDocument::isArray() const +{ + if (!d) + return false; + + Internal::Header *h = (Internal::Header *)d->rawData; + return h->root()->isArray(); +} + +/*! + Returns \c true if the document contains an object. + + \sa object(), isArray() + */ +bool JsonDocument::isObject() const +{ + if (!d) + return false; + + Internal::Header *h = (Internal::Header *)d->rawData; + return h->root()->isObject(); +} + +/*! + Returns the JsonObject contained in the document. + + Returns an empty object if the document contains an + array. + + \sa isObject(), array(), setObject() + */ +JsonObject JsonDocument::object() const +{ + if (d) { + Internal::Base *b = d->header->root(); + if (b->isObject()) + return JsonObject(d, static_cast(b)); + } + return JsonObject(); +} + +/*! + Returns the JsonArray contained in the document. + + Returns an empty array if the document contains an + object. + + \sa isArray(), object(), setArray() + */ +JsonArray JsonDocument::array() const +{ + if (d) { + Internal::Base *b = d->header->root(); + if (b->isArray()) + return JsonArray(d, static_cast(b)); + } + return JsonArray(); +} + +/*! + Sets \a object as the main object of this document. + + \sa setArray(), object() + */ +void JsonDocument::setObject(const JsonObject &object) +{ + if (d && !d->ref.deref()) + delete d; + + d = object.d; + + if (!d) { + d = new Internal::Data(0, JsonValue::Object); + } else if (d->compactionCounter || object.o != d->header->root()) { + JsonObject o(object); + if (d->compactionCounter) + o.compact(); + else + o.detach(); + d = o.d; + d->ref.ref(); + return; + } + d->ref.ref(); +} + +/*! + Sets \a array as the main object of this document. + + \sa setObject(), array() + */ +void JsonDocument::setArray(const JsonArray &array) +{ + if (d && !d->ref.deref()) + delete d; + + d = array.d; + + if (!d) { + d = new Internal::Data(0, JsonValue::Array); + } else if (d->compactionCounter || array.a != d->header->root()) { + JsonArray a(array); + if (d->compactionCounter) + a.compact(); + else + a.detach(); + d = a.d; + d->ref.ref(); + return; + } + d->ref.ref(); +} + +/*! + Returns \c true if the \a other document is equal to this document. + */ +bool JsonDocument::operator==(const JsonDocument &other) const +{ + if (d == other.d) + return true; + + if (!d || !other.d) + return false; + + if (d->header->root()->isArray() != other.d->header->root()->isArray()) + return false; + + if (d->header->root()->isObject()) + return JsonObject(d, static_cast(d->header->root())) + == JsonObject(other.d, static_cast(other.d->header->root())); + else + return JsonArray(d, static_cast(d->header->root())) + == JsonArray(other.d, static_cast(other.d->header->root())); +} + +/*! + \fn bool JsonDocument::operator!=(const JsonDocument &other) const + + returns \c true if \a other is not equal to this document + */ + +/*! + returns \c true if this document is null. + + Null documents are documents created through the default constructor. + + Documents created from UTF-8 encoded text or the binary format are + validated during parsing. If validation fails, the returned document + will also be null. + */ +bool JsonDocument::isNull() const +{ + return (d == 0); +} + + +static void objectContentToJson(const Object *o, std::string &json, int indent, bool compact); +static void arrayContentToJson(const Array *a, std::string &json, int indent, bool compact); + +static uint8_t hexdig(uint32_t u) +{ + return (u < 0xa ? '0' + u : 'a' + u - 0xa); +} + +static std::string escapedString(const std::string &in) +{ + std::string ba; + ba.reserve(in.length()); + + auto src = in.begin(); + auto end = in.end(); + + while (src != end) { + uint8_t u = (*src++); + if (u < 0x20 || u == 0x22 || u == 0x5c) { + ba.push_back('\\'); + switch (u) { + case 0x22: + ba.push_back('"'); + break; + case 0x5c: + ba.push_back('\\'); + break; + case 0x8: + ba.push_back('b'); + break; + case 0xc: + ba.push_back('f'); + break; + case 0xa: + ba.push_back('n'); + break; + case 0xd: + ba.push_back('r'); + break; + case 0x9: + ba.push_back('t'); + break; + default: + ba.push_back('u'); + ba.push_back('0'); + ba.push_back('0'); + ba.push_back(hexdig(u>>4)); + ba.push_back(hexdig(u & 0xf)); + } + } else { + ba.push_back(u); + } + } + + return ba; +} + +static void valueToJson(const Base *b, const Value &v, std::string &json, int indent, bool compact) +{ + JsonValue::Type type = (JsonValue::Type)(uint32_t)v.type; + switch (type) { + case JsonValue::Bool: + json += v.toBoolean() ? "true" : "false"; + break; + case JsonValue::Double: { + const double d = v.toDouble(b); + if (std::isfinite(d)) { + // +2 to format to ensure the expected precision + const int n = std::numeric_limits::digits10 + 2; + char buf[30] = {0}; + sprintf(buf, "%.*g", n, d); + // Hack: + if (buf[0] == '-' && buf[1] == '0' && buf[2] == '\0') + json += "0"; + else + json += buf; + } else { + json += "null"; // +INF || -INF || NaN (see RFC4627#section2.4) + } + break; + } + case JsonValue::String: + json += '"'; + json += escapedString(v.toString(b)); + json += '"'; + break; + case JsonValue::Array: + json += compact ? "[" : "[\n"; + arrayContentToJson(static_cast(v.base(b)), json, indent + (compact ? 0 : 1), compact); + json += std::string(4*indent, ' '); + json += ']'; + break; + case JsonValue::Object: + json += compact ? "{" : "{\n"; + objectContentToJson(static_cast(v.base(b)), json, indent + (compact ? 0 : 1), compact); + json += std::string(4*indent, ' '); + json += '}'; + break; + case JsonValue::Null: + default: + json += "null"; + } +} + +static void arrayContentToJson(const Array *a, std::string &json, int indent, bool compact) +{ + if (!a || !a->length) + return; + + std::string indentString(4*indent, ' '); + + uint32_t i = 0; + while (1) { + json += indentString; + valueToJson(a, a->at(i), json, indent, compact); + + if (++i == a->length) { + if (!compact) + json += '\n'; + break; + } + + json += compact ? "," : ",\n"; + } +} + +static void objectContentToJson(const Object *o, std::string &json, int indent, bool compact) +{ + if (!o || !o->length) + return; + + std::string indentString(4*indent, ' '); + + uint32_t i = 0; + while (1) { + Entry *e = o->entryAt(i); + json += indentString; + json += '"'; + json += escapedString(e->key()); + json += compact ? "\":" : "\": "; + valueToJson(o, e->value, json, indent, compact); + + if (++i == o->length) { + if (!compact) + json += '\n'; + break; + } + + json += compact ? "," : ",\n"; + } +} + +namespace Internal { + +void objectToJson(const Object *o, std::string &json, int indent, bool compact) +{ + json.reserve(json.size() + (o ? (int)o->size : 16)); + json += compact ? "{" : "{\n"; + objectContentToJson(o, json, indent + (compact ? 0 : 1), compact); + json += std::string(4*indent, ' '); + json += compact ? "}" : "}\n"; +} + +void arrayToJson(const Array *a, std::string &json, int indent, bool compact) +{ + json.reserve(json.size() + (a ? (int)a->size : 16)); + json += compact ? "[" : "[\n"; + arrayContentToJson(a, json, indent + (compact ? 0 : 1), compact); + json += std::string(4*indent, ' '); + json += compact ? "]" : "]\n"; +} + +} + + + +/*! + \class JsonParseError + \inmodule QtCore + \ingroup json + \ingroup shared + \reentrant + \since 5.0 + + \brief The JsonParseError class is used to report errors during JSON parsing. + + \sa {JSON Support in Qt}, {JSON Save Game Example} +*/ + +/*! + \enum JsonParseError::ParseError + + This enum describes the type of error that occurred during the parsing of a JSON document. + + \value NoError No error occurred + \value UnterminatedObject An object is not correctly terminated with a closing curly bracket + \value MissingNameSeparator A comma separating different items is missing + \value UnterminatedArray The array is not correctly terminated with a closing square bracket + \value MissingValueSeparator A colon separating keys from values inside objects is missing + \value IllegalValue The value is illegal + \value TerminationByNumber The input stream ended while parsing a number + \value IllegalNumber The number is not well formed + \value IllegalEscapeSequence An illegal escape sequence occurred in the input + \value IllegalUTF8String An illegal UTF8 sequence occurred in the input + \value UnterminatedString A string wasn't terminated with a quote + \value MissingObject An object was expected but couldn't be found + \value DeepNesting The JSON document is too deeply nested for the parser to parse it + \value DocumentTooLarge The JSON document is too large for the parser to parse it + \value GarbageAtEnd The parsed document contains additional garbage characters at the end + +*/ + +/*! + \variable JsonParseError::error + + Contains the type of the parse error. Is equal to JsonParseError::NoError if the document + was parsed correctly. + + \sa ParseError, errorString() +*/ + + +/*! + \variable JsonParseError::offset + + Contains the offset in the input string where the parse error occurred. + + \sa error, errorString() +*/ + +using namespace Internal; + +Parser::Parser(const char *json, int length) + : head(json), json(json), data(0), dataLength(0), current(0), nestingLevel(0), lastError(JsonParseError::NoError) +{ + end = json + length; +} + + + +/* + +begin-array = ws %x5B ws ; [ left square bracket + +begin-object = ws %x7B ws ; { left curly bracket + +end-array = ws %x5D ws ; ] right square bracket + +end-object = ws %x7D ws ; } right curly bracket + +name-separator = ws %x3A ws ; : colon + +value-separator = ws %x2C ws ; , comma + +Insignificant whitespace is allowed before or after any of the six +structural characters. + +ws = *( + %x20 / ; Space + %x09 / ; Horizontal tab + %x0A / ; Line feed or New line + %x0D ; Carriage return + ) + +*/ + +enum { + Space = 0x20, + Tab = 0x09, + LineFeed = 0x0a, + Return = 0x0d, + BeginArray = 0x5b, + BeginObject = 0x7b, + EndArray = 0x5d, + EndObject = 0x7d, + NameSeparator = 0x3a, + ValueSeparator = 0x2c, + Quote = 0x22 +}; + +void Parser::eatBOM() +{ + // eat UTF-8 byte order mark + if (end - json > 3 + && (unsigned char)json[0] == 0xef + && (unsigned char)json[1] == 0xbb + && (unsigned char)json[2] == 0xbf) + json += 3; +} + +bool Parser::eatSpace() +{ + while (json < end) { + if (*json > Space) + break; + if (*json != Space && + *json != Tab && + *json != LineFeed && + *json != Return) + break; + ++json; + } + return (json < end); +} + +char Parser::nextToken() +{ + if (!eatSpace()) + return 0; + char token = *json++; + switch (token) { + case BeginArray: + case BeginObject: + case NameSeparator: + case ValueSeparator: + case EndArray: + case EndObject: + eatSpace(); + case Quote: + break; + default: + token = 0; + break; + } + return token; +} + +/* + JSON-text = object / array +*/ +JsonDocument Parser::parse(JsonParseError *error) +{ +#ifdef PARSER_DEBUG + indent = 0; + std::cerr << ">>>>> parser begin"; +#endif + // allocate some space + dataLength = std::max(end - json, std::ptrdiff_t(256)); + data = (char *)malloc(dataLength); + + // fill in Header data + Header *h = (Header *)data; + h->tag = JsonDocument::BinaryFormatTag; + h->version = 1u; + + current = sizeof(Header); + + eatBOM(); + char token = nextToken(); + + DEBUG << std::hex << (uint32_t)token; + if (token == BeginArray) { + if (!parseArray()) + goto error; + } else if (token == BeginObject) { + if (!parseObject()) + goto error; + } else { + lastError = JsonParseError::IllegalValue; + goto error; + } + + eatSpace(); + if (json < end) { + lastError = JsonParseError::GarbageAtEnd; + goto error; + } + + END; + { + if (error) { + error->offset = 0; + error->error = JsonParseError::NoError; + } + Data *d = new Data(data, current); + return JsonDocument(d); + } + +error: +#ifdef PARSER_DEBUG + std::cerr << ">>>>> parser error"; +#endif + if (error) { + error->offset = json - head; + error->error = lastError; + } + free(data); + return JsonDocument(); +} + + +void Parser::ParsedObject::insert(uint32_t offset) +{ + const Entry *newEntry = reinterpret_cast(parser->data + objectPosition + offset); + size_t min = 0; + size_t n = offsets.size(); + while (n > 0) { + int half = n >> 1; + int middle = min + half; + if (*entryAt(middle) >= *newEntry) { + n = half; + } else { + min = middle + 1; + n -= half + 1; + } + } + if (min < offsets.size() && *entryAt(min) == *newEntry) { + offsets[min] = offset; + } else { + offsets.insert(offsets.begin() + min, offset); + } +} + +/* + object = begin-object [ member *( value-separator member ) ] + end-object +*/ + +bool Parser::parseObject() +{ + if (++nestingLevel > nestingLimit) { + lastError = JsonParseError::DeepNesting; + return false; + } + + int objectOffset = reserveSpace(sizeof(Object)); + BEGIN << "parseObject pos=" << objectOffset << current << json; + + ParsedObject parsedObject(this, objectOffset); + + char token = nextToken(); + while (token == Quote) { + int off = current - objectOffset; + if (!parseMember(objectOffset)) + return false; + parsedObject.insert(off); + token = nextToken(); + if (token != ValueSeparator) + break; + token = nextToken(); + if (token == EndObject) { + lastError = JsonParseError::MissingObject; + return false; + } + } + + DEBUG << "end token=" << token; + if (token != EndObject) { + lastError = JsonParseError::UnterminatedObject; + return false; + } + + DEBUG << "numEntries" << parsedObject.offsets.size(); + int table = objectOffset; + // finalize the object + if (parsedObject.offsets.size()) { + int tableSize = parsedObject.offsets.size()*sizeof(uint32_t); + table = reserveSpace(tableSize); + memcpy(data + table, &*parsedObject.offsets.begin(), tableSize); + } + + Object *o = (Object *)(data + objectOffset); + o->tableOffset = table - objectOffset; + o->size = current - objectOffset; + o->is_object = true; + o->length = parsedObject.offsets.size(); + + DEBUG << "current=" << current; + END; + + --nestingLevel; + return true; +} + +/* + member = string name-separator value +*/ +bool Parser::parseMember(int baseOffset) +{ + int entryOffset = reserveSpace(sizeof(Entry)); + BEGIN << "parseMember pos=" << entryOffset; + + if (!parseString()) + return false; + char token = nextToken(); + if (token != NameSeparator) { + lastError = JsonParseError::MissingNameSeparator; + return false; + } + Value val; + if (!parseValue(&val, baseOffset)) + return false; + + // finalize the entry + Entry *e = (Entry *)(data + entryOffset); + e->value = val; + + END; + return true; +} + +/* + array = begin-array [ value *( value-separator value ) ] end-array +*/ +bool Parser::parseArray() +{ + BEGIN << "parseArray"; + + if (++nestingLevel > nestingLimit) { + lastError = JsonParseError::DeepNesting; + return false; + } + + int arrayOffset = reserveSpace(sizeof(Array)); + + std::vector values; + values.reserve(64); + + if (!eatSpace()) { + lastError = JsonParseError::UnterminatedArray; + return false; + } + if (*json == EndArray) { + nextToken(); + } else { + while (1) { + Value val; + if (!parseValue(&val, arrayOffset)) + return false; + values.push_back(val); + char token = nextToken(); + if (token == EndArray) + break; + else if (token != ValueSeparator) { + if (!eatSpace()) + lastError = JsonParseError::UnterminatedArray; + else + lastError = JsonParseError::MissingValueSeparator; + return false; + } + } + } + + DEBUG << "size =" << values.size(); + int table = arrayOffset; + // finalize the object + if (values.size()) { + int tableSize = values.size()*sizeof(Value); + table = reserveSpace(tableSize); + memcpy(data + table, values.data(), tableSize); + } + + Array *a = (Array *)(data + arrayOffset); + a->tableOffset = table - arrayOffset; + a->size = current - arrayOffset; + a->is_object = false; + a->length = values.size(); + + DEBUG << "current=" << current; + END; + + --nestingLevel; + return true; +} + +/* +value = false / null / true / object / array / number / string + +*/ + +bool Parser::parseValue(Value *val, int baseOffset) +{ + BEGIN << "parse Value" << json; + val->_dummy = 0; + + switch (*json++) { + case 'n': + if (end - json < 4) { + lastError = JsonParseError::IllegalValue; + return false; + } + if (*json++ == 'u' && + *json++ == 'l' && + *json++ == 'l') { + val->type = JsonValue::Null; + DEBUG << "value: null"; + END; + return true; + } + lastError = JsonParseError::IllegalValue; + return false; + case 't': + if (end - json < 4) { + lastError = JsonParseError::IllegalValue; + return false; + } + if (*json++ == 'r' && + *json++ == 'u' && + *json++ == 'e') { + val->type = JsonValue::Bool; + val->value = true; + DEBUG << "value: true"; + END; + return true; + } + lastError = JsonParseError::IllegalValue; + return false; + case 'f': + if (end - json < 5) { + lastError = JsonParseError::IllegalValue; + return false; + } + if (*json++ == 'a' && + *json++ == 'l' && + *json++ == 's' && + *json++ == 'e') { + val->type = JsonValue::Bool; + val->value = false; + DEBUG << "value: false"; + END; + return true; + } + lastError = JsonParseError::IllegalValue; + return false; + case Quote: { + val->type = JsonValue::String; + if (current - baseOffset >= Value::MaxSize) { + lastError = JsonParseError::DocumentTooLarge; + return false; + } + val->value = current - baseOffset; + if (!parseString()) + return false; + val->intValue = false; + DEBUG << "value: string"; + END; + return true; + } + case BeginArray: + val->type = JsonValue::Array; + if (current - baseOffset >= Value::MaxSize) { + lastError = JsonParseError::DocumentTooLarge; + return false; + } + val->value = current - baseOffset; + if (!parseArray()) + return false; + DEBUG << "value: array"; + END; + return true; + case BeginObject: + val->type = JsonValue::Object; + if (current - baseOffset >= Value::MaxSize) { + lastError = JsonParseError::DocumentTooLarge; + return false; + } + val->value = current - baseOffset; + if (!parseObject()) + return false; + DEBUG << "value: object"; + END; + return true; + case EndArray: + lastError = JsonParseError::MissingObject; + return false; + default: + --json; + if (!parseNumber(val, baseOffset)) + return false; + DEBUG << "value: number"; + END; + } + + return true; +} + + + + + +/* + number = [ minus ] int [ frac ] [ exp ] + decimal-point = %x2E ; . + digit1-9 = %x31-39 ; 1-9 + e = %x65 / %x45 ; e E + exp = e [ minus / plus ] 1*DIGIT + frac = decimal-point 1*DIGIT + int = zero / ( digit1-9 *DIGIT ) + minus = %x2D ; - + plus = %x2B ; + + zero = %x30 ; 0 + +*/ + +bool Parser::parseNumber(Value *val, int baseOffset) +{ + BEGIN << "parseNumber" << json; + val->type = JsonValue::Double; + + const char *start = json; + bool isInt = true; + + // minus + if (json < end && *json == '-') + ++json; + + // int = zero / ( digit1-9 *DIGIT ) + if (json < end && *json == '0') { + ++json; + } else { + while (json < end && *json >= '0' && *json <= '9') + ++json; + } + + // frac = decimal-point 1*DIGIT + if (json < end && *json == '.') { + isInt = false; + ++json; + while (json < end && *json >= '0' && *json <= '9') + ++json; + } + + // exp = e [ minus / plus ] 1*DIGIT + if (json < end && (*json == 'e' || *json == 'E')) { + isInt = false; + ++json; + if (json < end && (*json == '-' || *json == '+')) + ++json; + while (json < end && *json >= '0' && *json <= '9') + ++json; + } + + if (json >= end) { + lastError = JsonParseError::TerminationByNumber; + return false; + } + + if (isInt) { + char *endptr = const_cast(json); + long long int n = strtoll(start, &endptr, 0); + if (endptr != start && n < (1<<25) && n > -(1<<25)) { + val->int_value = int(n); + val->intValue = true; + END; + return true; + } + } + + char *endptr = const_cast(json); + double d = strtod(start, &endptr); + + if (start == endptr || isinf(d)) { + lastError = JsonParseError::IllegalNumber; + return false; + } + + int pos = reserveSpace(sizeof(double)); + memcpy(data + pos, &d, sizeof(double)); + if (current - baseOffset >= Value::MaxSize) { + lastError = JsonParseError::DocumentTooLarge; + return false; + } + val->value = pos - baseOffset; + val->intValue = false; + + END; + return true; +} + +/* + + string = quotation-mark *char quotation-mark + + char = unescaped / + escape ( + %x22 / ; " quotation mark U+0022 + %x5C / ; \ reverse solidus U+005C + %x2F / ; / solidus U+002F + %x62 / ; b backspace U+0008 + %x66 / ; f form feed U+000C + %x6E / ; n line feed U+000A + %x72 / ; r carriage return U+000D + %x74 / ; t tab U+0009 + %x75 4HEXDIG ) ; uXXXX U+XXXX + + escape = %x5C ; \ + + quotation-mark = %x22 ; " + + unescaped = %x20-21 / %x23-5B / %x5D-10FFFF + */ +static bool addHexDigit(char digit, uint32_t *result) +{ + *result <<= 4; + if (digit >= '0' && digit <= '9') + *result |= (digit - '0'); + else if (digit >= 'a' && digit <= 'f') + *result |= (digit - 'a') + 10; + else if (digit >= 'A' && digit <= 'F') + *result |= (digit - 'A') + 10; + else + return false; + return true; +} + +bool Parser::parseEscapeSequence() +{ + DEBUG << "scan escape" << (char)*json; + const char escaped = *json++; + switch (escaped) { + case '"': + addChar('"'); break; + case '\\': + addChar('\\'); break; + case '/': + addChar('/'); break; + case 'b': + addChar(0x8); break; + case 'f': + addChar(0xc); break; + case 'n': + addChar(0xa); break; + case 'r': + addChar(0xd); break; + case 't': + addChar(0x9); break; + case 'u': { + uint32_t c = 0; + if (json > end - 4) + return false; + for (int i = 0; i < 4; ++i) { + if (!addHexDigit(*json, &c)) + return false; + ++json; + } + if (c < 0x80) { + addChar(c); + break; + } + if (c < 0x800) { + addChar(192 + c / 64); + addChar(128 + c % 64); + break; + } + if (c - 0xd800u < 0x800) { + return false; + } + if (c < 0x10000) { + addChar(224 + c / 4096); + addChar(128 + c / 64 % 64); + addChar(128 + c % 64); + break; + } + if (c < 0x110000) { + addChar(240 + c / 262144); + addChar(128 + c / 4096 % 64); + addChar(128 + c / 64 % 64); + addChar(128 + c % 64); + break; + } + return false; + } + default: + // this is not as strict as one could be, but allows for more Json files + // to be parsed correctly. + addChar(escaped); + break; + } + return true; +} + +bool Parser::parseString() +{ + const char *inStart = json; + + // First try quick pass without escapes. + if (true) { + while (1) { + if (json >= end) { + ++json; + lastError = JsonParseError::UnterminatedString; + return false; + } + + const char c = *json; + if (c == '"') { + // write string length and padding. + const int len = json - inStart; + const int pos = reserveSpace(4 + alignedSize(len)); + toInternal(data + pos, inStart, len); + END; + + ++json; + return true; + } + + if (c == '\\') + break; + ++json; + } + } + + // Try again with escapes. + const int outStart = reserveSpace(4); + json = inStart; + while (1) { + if (json >= end) { + ++json; + lastError = JsonParseError::UnterminatedString; + return false; + } + + if (*json == '"') { + ++json; + // write string length and padding. + *(int *)(data + outStart) = current - outStart - 4; + reserveSpace((4 - current) & 3); + END; + return true; + } + + if (*json == '\\') { + ++json; + if (json >= end || !parseEscapeSequence()) { + lastError = JsonParseError::IllegalEscapeSequence; + return false; + } + } else { + addChar(*json++); + } + } +} + +namespace Internal { + +static const Base emptyArray = { sizeof(Base), { 0 }, 0 }; +static const Base emptyObject = { sizeof(Base), { 0 }, 0 }; + + +void Data::compact() +{ + // assert(sizeof(Value) == sizeof(offset)); + + if (!compactionCounter) + return; + + Base *base = header->root(); + int reserve = 0; + if (base->is_object) { + Object *o = static_cast(base); + for (int i = 0; i < (int)o->length; ++i) + reserve += o->entryAt(i)->usedStorage(o); + } else { + Array *a = static_cast(base); + for (int i = 0; i < (int)a->length; ++i) + reserve += (*a)[i].usedStorage(a); + } + + int size = sizeof(Base) + reserve + base->length*sizeof(offset); + int alloc = sizeof(Header) + size; + Header *h = (Header *) malloc(alloc); + h->tag = JsonDocument::BinaryFormatTag; + h->version = 1; + Base *b = h->root(); + b->size = size; + b->is_object = header->root()->is_object; + b->length = base->length; + b->tableOffset = reserve + sizeof(Array); + + int offset = sizeof(Base); + if (b->is_object) { + Object *o = static_cast(base); + Object *no = static_cast(b); + + for (int i = 0; i < (int)o->length; ++i) { + no->table()[i] = offset; + + const Entry *e = o->entryAt(i); + Entry *ne = no->entryAt(i); + int s = e->size(); + memcpy(ne, e, s); + offset += s; + int dataSize = e->value.usedStorage(o); + if (dataSize) { + memcpy((char *)no + offset, e->value.data(o), dataSize); + ne->value.value = offset; + offset += dataSize; + } + } + } else { + Array *a = static_cast(base); + Array *na = static_cast(b); + + for (int i = 0; i < (int)a->length; ++i) { + const Value &v = (*a)[i]; + Value &nv = (*na)[i]; + nv = v; + int dataSize = v.usedStorage(a); + if (dataSize) { + memcpy((char *)na + offset, v.data(a), dataSize); + nv.value = offset; + offset += dataSize; + } + } + } + // assert(offset == (int)b->tableOffset); + + free(header); + header = h; + this->alloc = alloc; + compactionCounter = 0; +} + +bool Data::valid() const +{ + if (header->tag != JsonDocument::BinaryFormatTag || header->version != 1u) + return false; + + bool res = false; + if (header->root()->is_object) + res = static_cast(header->root())->isValid(); + else + res = static_cast(header->root())->isValid(); + + return res; +} + + +int Base::reserveSpace(uint32_t dataSize, int posInTable, uint32_t numItems, bool replace) +{ + // assert(posInTable >= 0 && posInTable <= (int)length); + if (size + dataSize >= Value::MaxSize) { + fprintf(stderr, "Json: Document too large to store in data structure %d %d %d\n", (uint32_t)size, dataSize, Value::MaxSize); + return 0; + } + + offset off = tableOffset; + // move table to new position + if (replace) { + memmove((char *)(table()) + dataSize, table(), length*sizeof(offset)); + } else { + memmove((char *)(table() + posInTable + numItems) + dataSize, table() + posInTable, (length - posInTable)*sizeof(offset)); + memmove((char *)(table()) + dataSize, table(), posInTable*sizeof(offset)); + } + tableOffset += dataSize; + for (int i = 0; i < (int)numItems; ++i) + table()[posInTable + i] = off; + size += dataSize; + if (!replace) { + length += numItems; + size += numItems * sizeof(offset); + } + return off; +} + +void Base::removeItems(int pos, int numItems) +{ + // assert(pos >= 0 && pos <= (int)length); + if (pos + numItems < (int)length) + memmove(table() + pos, table() + pos + numItems, (length - pos - numItems)*sizeof(offset)); + length -= numItems; +} + +int Object::indexOf(const std::string &key, bool *exists) +{ + int min = 0; + int n = length; + while (n > 0) { + int half = n >> 1; + int middle = min + half; + if (*entryAt(middle) >= key) { + n = half; + } else { + min = middle + 1; + n -= half + 1; + } + } + if (min < (int)length && *entryAt(min) == key) { + *exists = true; + return min; + } + *exists = false; + return min; +} + +bool Object::isValid() const +{ + if (tableOffset + length*sizeof(offset) > size) + return false; + + std::string lastKey; + for (uint32_t i = 0; i < length; ++i) { + offset entryOffset = table()[i]; + if (entryOffset + sizeof(Entry) >= tableOffset) + return false; + Entry *e = entryAt(i); + int s = e->size(); + if (table()[i] + s > tableOffset) + return false; + std::string key = e->key(); + if (key < lastKey) + return false; + if (!e->value.isValid(this)) + return false; + lastKey = key; + } + return true; +} + +bool Array::isValid() const +{ + if (tableOffset + length*sizeof(offset) > size) + return false; + + for (uint32_t i = 0; i < length; ++i) { + if (!at(i).isValid(this)) + return false; + } + return true; +} + + +bool Entry::operator==(const std::string &key) const +{ + return shallowKey() == key; +} + +bool Entry::operator==(const Entry &other) const +{ + return shallowKey() == other.shallowKey(); +} + +bool Entry::operator>=(const Entry &other) const +{ + return shallowKey() >= other.shallowKey(); +} + + +int Value::usedStorage(const Base *b) const +{ + int s = 0; + switch (type) { + case JsonValue::Double: + if (intValue) + break; + s = sizeof(double); + break; + case JsonValue::String: { + char *d = data(b); + s = sizeof(int) + (*(int *)d); + break; + } + case JsonValue::Array: + case JsonValue::Object: + s = base(b)->size; + break; + case JsonValue::Null: + case JsonValue::Bool: + default: + break; + } + return alignedSize(s); +} + +bool Value::isValid(const Base *b) const +{ + int offset = 0; + switch (type) { + case JsonValue::Double: + if (intValue) + break; + // fall through + case JsonValue::String: + case JsonValue::Array: + case JsonValue::Object: + offset = value; + break; + case JsonValue::Null: + case JsonValue::Bool: + default: + break; + } + + if (!offset) + return true; + if (offset + sizeof(uint32_t) > b->tableOffset) + return false; + + int s = usedStorage(b); + if (!s) + return true; + if (s < 0 || offset + s > (int)b->tableOffset) + return false; + if (type == JsonValue::Array) + return static_cast(base(b))->isValid(); + if (type == JsonValue::Object) + return static_cast(base(b))->isValid(); + return true; +} + +/*! + \internal + */ +int Value::requiredStorage(JsonValue &v, bool *compressed) +{ + *compressed = false; + switch (v.t) { + case JsonValue::Double: + if (Internal::compressedNumber(v.dbl) != INT_MAX) { + *compressed = true; + return 0; + } + return sizeof(double); + case JsonValue::String: { + std::string s = v.toString().data(); + *compressed = false; + return Internal::qStringSize(s); + } + case JsonValue::Array: + case JsonValue::Object: + if (v.d && v.d->compactionCounter) { + v.detach(); + v.d->compact(); + v.base = static_cast(v.d->header->root()); + } + return v.base ? v.base->size : sizeof(Internal::Base); + case JsonValue::Undefined: + case JsonValue::Null: + case JsonValue::Bool: + break; + } + return 0; +} + +/*! + \internal + */ +uint32_t Value::valueToStore(const JsonValue &v, uint32_t offset) +{ + switch (v.t) { + case JsonValue::Undefined: + case JsonValue::Null: + break; + case JsonValue::Bool: + return v.b; + case JsonValue::Double: { + int c = Internal::compressedNumber(v.dbl); + if (c != INT_MAX) + return c; + } + // fall through + case JsonValue::String: + case JsonValue::Array: + case JsonValue::Object: + return offset; + } + return 0; +} + +/*! + \internal + */ + +void Value::copyData(const JsonValue &v, char *dest, bool compressed) +{ + switch (v.t) { + case JsonValue::Double: + if (!compressed) + memcpy(dest, &v.ui, 8); + break; + case JsonValue::String: { + std::string str = v.toString(); + Internal::copyString(dest, str); + break; + } + case JsonValue::Array: + case JsonValue::Object: { + const Internal::Base *b = v.base; + if (!b) + b = (v.t == JsonValue::Array ? &emptyArray : &emptyObject); + memcpy(dest, b, b->size); + break; + } + default: + break; + } +} + +} // namespace Internal +} // namespace Json diff --git a/src/shared/json/json.h b/src/shared/json/json.h new file mode 100644 index 00000000000..4b9c543ff0c --- /dev/null +++ b/src/shared/json/json.h @@ -0,0 +1,583 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef JSONVALUE_H +#define JSONVALUE_H + +#include +#include +#include +#include + +namespace Json { + +class JsonArray; +class JsonObject; + +namespace Internal { +class Data; +class Base; +class Object; +class Header; +class Array; +class Value; +class Entry; +class SharedString; +class Parser; +} + +class JsonValue +{ +public: + enum Type { + Null = 0x0, + Bool = 0x1, + Double = 0x2, + String = 0x3, + Array = 0x4, + Object = 0x5, + Undefined = 0x80 + }; + + JsonValue(Type = Null); + JsonValue(bool b); + JsonValue(double n); + JsonValue(int n); + JsonValue(int64_t n); + JsonValue(const std::string &s); + JsonValue(const char *s); + JsonValue(const JsonArray &a); + JsonValue(const JsonObject &o); + + ~JsonValue(); + + JsonValue(const JsonValue &other); + JsonValue &operator =(const JsonValue &other); + + Type type() const { return t; } + bool isNull() const { return t == Null; } + bool isBool() const { return t == Bool; } + bool isDouble() const { return t == Double; } + bool isString() const { return t == String; } + bool isArray() const { return t == Array; } + bool isObject() const { return t == Object; } + bool isUndefined() const { return t == Undefined; } + + bool toBool(bool defaultValue = false) const; + int toInt(int defaultValue = 0) const; + double toDouble(double defaultValue = 0) const; + std::string toString(const std::string &defaultValue = std::string()) const; + JsonArray toArray() const; + JsonArray toArray(const JsonArray &defaultValue) const; + JsonObject toObject() const; + JsonObject toObject(const JsonObject &defaultValue) const; + + bool operator==(const JsonValue &other) const; + bool operator!=(const JsonValue &other) const; + +private: + // avoid implicit conversions from char * to bool + JsonValue(const void *) : t(Null) {} + friend class Internal::Value; + friend class JsonArray; + friend class JsonObject; + + JsonValue(Internal::Data *d, Internal::Base *b, const Internal::Value& v); + + void detach(); + + union { + uint64_t ui; + bool b; + double dbl; + Internal::SharedString *stringData; + Internal::Base *base; + }; + Internal::Data *d; // needed for Objects and Arrays + Type t; +}; + +class JsonValueRef +{ +public: + JsonValueRef(JsonArray *array, int idx) + : a(array), is_object(false), index(idx) {} + JsonValueRef(JsonObject *object, int idx) + : o(object), is_object(true), index(idx) {} + + operator JsonValue() const { return toValue(); } + JsonValueRef &operator=(const JsonValue &val); + JsonValueRef &operator=(const JsonValueRef &val); + + JsonValue::Type type() const { return toValue().type(); } + bool isNull() const { return type() == JsonValue::Null; } + bool isBool() const { return type() == JsonValue::Bool; } + bool isDouble() const { return type() == JsonValue::Double; } + bool isString() const { return type() == JsonValue::String; } + bool isArray() const { return type() == JsonValue::Array; } + bool isObject() const { return type() == JsonValue::Object; } + bool isUndefined() const { return type() == JsonValue::Undefined; } + + std::string toString() const { return toValue().toString(); } + JsonArray toArray() const; + JsonObject toObject() const; + + bool toBool(bool defaultValue = false) const { return toValue().toBool(defaultValue); } + int toInt(int defaultValue = 0) const { return toValue().toInt(defaultValue); } + double toDouble(double defaultValue = 0) const { return toValue().toDouble(defaultValue); } + std::string toString(const std::string &defaultValue) const { return toValue().toString(defaultValue); } + + bool operator==(const JsonValue &other) const { return toValue() == other; } + bool operator!=(const JsonValue &other) const { return toValue() != other; } + +private: + JsonValue toValue() const; + + union { + JsonArray *a; + JsonObject *o; + }; + uint32_t is_object : 1; + uint32_t index : 31; +}; + +class JsonValuePtr +{ + JsonValue value; +public: + explicit JsonValuePtr(const JsonValue& val) + : value(val) {} + + JsonValue& operator*() { return value; } + JsonValue* operator->() { return &value; } +}; + +class JsonValueRefPtr +{ + JsonValueRef valueRef; +public: + JsonValueRefPtr(JsonArray *array, int idx) + : valueRef(array, idx) {} + JsonValueRefPtr(JsonObject *object, int idx) + : valueRef(object, idx) {} + + JsonValueRef& operator*() { return valueRef; } + JsonValueRef* operator->() { return &valueRef; } +}; + + + +class JsonArray +{ +public: + JsonArray(); + JsonArray(std::initializer_list args); + + ~JsonArray(); + + JsonArray(const JsonArray &other); + JsonArray &operator=(const JsonArray &other); + + int size() const; + int count() const { return size(); } + + bool isEmpty() const; + JsonValue at(int i) const; + JsonValue first() const; + JsonValue last() const; + + void prepend(const JsonValue &value); + void append(const JsonValue &value); + void removeAt(int i); + JsonValue takeAt(int i); + void removeFirst() { removeAt(0); } + void removeLast() { removeAt(size() - 1); } + + void insert(int i, const JsonValue &value); + void replace(int i, const JsonValue &value); + + bool contains(const JsonValue &element) const; + JsonValueRef operator[](int i); + JsonValue operator[](int i) const; + + bool operator==(const JsonArray &other) const; + bool operator!=(const JsonArray &other) const; + + class const_iterator; + + class iterator { + public: + JsonArray *a; + int i; + typedef std::random_access_iterator_tag iterator_category; + typedef int difference_type; + typedef JsonValue value_type; + typedef JsonValueRef reference; + typedef JsonValueRefPtr pointer; + + iterator() : a(nullptr), i(0) { } + explicit iterator(JsonArray *array, int index) : a(array), i(index) { } + + JsonValueRef operator*() const { return JsonValueRef(a, i); } + JsonValueRefPtr operator->() const { return JsonValueRefPtr(a, i); } + JsonValueRef operator[](int j) const { return JsonValueRef(a, i + j); } + + bool operator==(const iterator &o) const { return i == o.i; } + bool operator!=(const iterator &o) const { return i != o.i; } + bool operator<(const iterator& other) const { return i < other.i; } + bool operator<=(const iterator& other) const { return i <= other.i; } + bool operator>(const iterator& other) const { return i > other.i; } + bool operator>=(const iterator& other) const { return i >= other.i; } + bool operator==(const const_iterator &o) const { return i == o.i; } + bool operator!=(const const_iterator &o) const { return i != o.i; } + bool operator<(const const_iterator& other) const { return i < other.i; } + bool operator<=(const const_iterator& other) const { return i <= other.i; } + bool operator>(const const_iterator& other) const { return i > other.i; } + bool operator>=(const const_iterator& other) const { return i >= other.i; } + iterator &operator++() { ++i; return *this; } + iterator operator++(int) { iterator n = *this; ++i; return n; } + iterator &operator--() { i--; return *this; } + iterator operator--(int) { iterator n = *this; i--; return n; } + iterator &operator+=(int j) { i+=j; return *this; } + iterator &operator-=(int j) { i-=j; return *this; } + iterator operator+(int j) const { return iterator(a, i+j); } + iterator operator-(int j) const { return iterator(a, i-j); } + int operator-(iterator j) const { return i - j.i; } + }; + friend class iterator; + + class const_iterator { + public: + const JsonArray *a; + int i; + typedef std::random_access_iterator_tag iterator_category; + typedef std::ptrdiff_t difference_type; + typedef JsonValue value_type; + typedef JsonValue reference; + typedef JsonValuePtr pointer; + + const_iterator() : a(nullptr), i(0) { } + explicit const_iterator(const JsonArray *array, int index) : a(array), i(index) { } + const_iterator(const iterator &o) : a(o.a), i(o.i) {} + + JsonValue operator*() const { return a->at(i); } + JsonValuePtr operator->() const { return JsonValuePtr(a->at(i)); } + JsonValue operator[](int j) const { return a->at(i+j); } + bool operator==(const const_iterator &o) const { return i == o.i; } + bool operator!=(const const_iterator &o) const { return i != o.i; } + bool operator<(const const_iterator& other) const { return i < other.i; } + bool operator<=(const const_iterator& other) const { return i <= other.i; } + bool operator>(const const_iterator& other) const { return i > other.i; } + bool operator>=(const const_iterator& other) const { return i >= other.i; } + const_iterator &operator++() { ++i; return *this; } + const_iterator operator++(int) { const_iterator n = *this; ++i; return n; } + const_iterator &operator--() { i--; return *this; } + const_iterator operator--(int) { const_iterator n = *this; i--; return n; } + const_iterator &operator+=(int j) { i+=j; return *this; } + const_iterator &operator-=(int j) { i-=j; return *this; } + const_iterator operator+(int j) const { return const_iterator(a, i+j); } + const_iterator operator-(int j) const { return const_iterator(a, i-j); } + int operator-(const_iterator j) const { return i - j.i; } + }; + friend class const_iterator; + + // stl style + iterator begin() { detach(); return iterator(this, 0); } + const_iterator begin() const { return const_iterator(this, 0); } + const_iterator constBegin() const { return const_iterator(this, 0); } + iterator end() { detach(); return iterator(this, size()); } + const_iterator end() const { return const_iterator(this, size()); } + const_iterator constEnd() const { return const_iterator(this, size()); } + iterator insert(iterator before, const JsonValue &value) { insert(before.i, value); return before; } + iterator erase(iterator it) { removeAt(it.i); return it; } + + void push_back(const JsonValue &t) { append(t); } + void push_front(const JsonValue &t) { prepend(t); } + void pop_front() { removeFirst(); } + void pop_back() { removeLast(); } + bool empty() const { return isEmpty(); } + typedef int size_type; + typedef JsonValue value_type; + typedef value_type *pointer; + typedef const value_type *const_pointer; + typedef JsonValueRef reference; + typedef JsonValue const_reference; + typedef int difference_type; + +private: + friend class Internal::Data; + friend class JsonValue; + friend class JsonDocument; + + JsonArray(Internal::Data *data, Internal::Array *array); + void compact(); + void detach(uint32_t reserve = 0); + + Internal::Data *d; + Internal::Array *a; +}; + + +class JsonObject +{ +public: + JsonObject(); + JsonObject(std::initializer_list > args); + ~JsonObject(); + + JsonObject(const JsonObject &other); + JsonObject &operator =(const JsonObject &other); + + typedef std::vector Keys; + Keys keys() const; + int size() const; + int count() const { return size(); } + int length() const { return size(); } + bool isEmpty() const; + + JsonValue value(const std::string &key) const; + JsonValue operator[] (const std::string &key) const; + JsonValueRef operator[] (const std::string &key); + + void remove(const std::string &key); + JsonValue take(const std::string &key); + bool contains(const std::string &key) const; + + bool operator==(const JsonObject &other) const; + bool operator!=(const JsonObject &other) const; + + class const_iterator; + + class iterator + { + friend class const_iterator; + friend class JsonObject; + JsonObject *o; + int i; + + public: + typedef std::bidirectional_iterator_tag iterator_category; + typedef int difference_type; + typedef JsonValue value_type; + typedef JsonValueRef reference; + + iterator() : o(nullptr), i(0) {} + iterator(JsonObject *obj, int index) : o(obj), i(index) {} + + std::string key() const { return o->keyAt(i); } + JsonValueRef value() const { return JsonValueRef(o, i); } + JsonValueRef operator*() const { return JsonValueRef(o, i); } + JsonValueRefPtr operator->() const { return JsonValueRefPtr(o, i); } + bool operator==(const iterator &other) const { return i == other.i; } + bool operator!=(const iterator &other) const { return i != other.i; } + + iterator &operator++() { ++i; return *this; } + iterator operator++(int) { iterator r = *this; ++i; return r; } + iterator &operator--() { --i; return *this; } + iterator operator--(int) { iterator r = *this; --i; return r; } + iterator operator+(int j) const + { iterator r = *this; r.i += j; return r; } + iterator operator-(int j) const { return operator+(-j); } + iterator &operator+=(int j) { i += j; return *this; } + iterator &operator-=(int j) { i -= j; return *this; } + + public: + bool operator==(const const_iterator &other) const { return i == other.i; } + bool operator!=(const const_iterator &other) const { return i != other.i; } + }; + friend class iterator; + + class const_iterator + { + friend class iterator; + const JsonObject *o; + int i; + + public: + typedef std::bidirectional_iterator_tag iterator_category; + typedef int difference_type; + typedef JsonValue value_type; + typedef JsonValue reference; + + const_iterator() : o(nullptr), i(0) {} + const_iterator(const JsonObject *obj, int index) + : o(obj), i(index) {} + const_iterator(const iterator &other) + : o(other.o), i(other.i) {} + + std::string key() const { return o->keyAt(i); } + JsonValue value() const { return o->valueAt(i); } + JsonValue operator*() const { return o->valueAt(i); } + JsonValuePtr operator->() const { return JsonValuePtr(o->valueAt(i)); } + bool operator==(const const_iterator &other) const { return i == other.i; } + bool operator!=(const const_iterator &other) const { return i != other.i; } + + const_iterator &operator++() { ++i; return *this; } + const_iterator operator++(int) { const_iterator r = *this; ++i; return r; } + const_iterator &operator--() { --i; return *this; } + const_iterator operator--(int) { const_iterator r = *this; --i; return r; } + const_iterator operator+(int j) const + { const_iterator r = *this; r.i += j; return r; } + const_iterator operator-(int j) const { return operator+(-j); } + const_iterator &operator+=(int j) { i += j; return *this; } + const_iterator &operator-=(int j) { i -= j; return *this; } + + bool operator==(const iterator &other) const { return i == other.i; } + bool operator!=(const iterator &other) const { return i != other.i; } + }; + friend class const_iterator; + + // STL style + iterator begin() { detach(); return iterator(this, 0); } + const_iterator begin() const { return const_iterator(this, 0); } + const_iterator constBegin() const { return const_iterator(this, 0); } + iterator end() { detach(); return iterator(this, size()); } + const_iterator end() const { return const_iterator(this, size()); } + const_iterator constEnd() const { return const_iterator(this, size()); } + iterator erase(iterator it); + + // more Qt + iterator find(const std::string &key); + const_iterator find(const std::string &key) const { return constFind(key); } + const_iterator constFind(const std::string &key) const; + iterator insert(const std::string &key, const JsonValue &value); + + // STL compatibility + typedef JsonValue mapped_type; + typedef std::string key_type; + typedef int size_type; + + bool empty() const { return isEmpty(); } + +private: + friend class Internal::Data; + friend class JsonValue; + friend class JsonDocument; + friend class JsonValueRef; + + JsonObject(Internal::Data *data, Internal::Object *object); + void detach(uint32_t reserve = 0); + void compact(); + + std::string keyAt(int i) const; + JsonValue valueAt(int i) const; + void setValueAt(int i, const JsonValue &val); + + Internal::Data *d; + Internal::Object *o; +}; + +struct JsonParseError +{ + enum ParseError { + NoError = 0, + UnterminatedObject, + MissingNameSeparator, + UnterminatedArray, + MissingValueSeparator, + IllegalValue, + TerminationByNumber, + IllegalNumber, + IllegalEscapeSequence, + IllegalUTF8String, + UnterminatedString, + MissingObject, + DeepNesting, + DocumentTooLarge, + GarbageAtEnd + }; + + int offset; + ParseError error; +}; + +class JsonDocument +{ +public: + static const uint32_t BinaryFormatTag = ('q') | ('b' << 8) | ('j' << 16) | ('s' << 24); + JsonDocument(); + explicit JsonDocument(const JsonObject &object); + explicit JsonDocument(const JsonArray &array); + ~JsonDocument(); + + JsonDocument(const JsonDocument &other); + JsonDocument &operator =(const JsonDocument &other); + + enum DataValidation { + Validate, + BypassValidation + }; + + static JsonDocument fromRawData(const char *data, int size, DataValidation validation = Validate); + const char *rawData(int *size) const; + + static JsonDocument fromBinaryData(const std::string &data, DataValidation validation = Validate); + std::string toBinaryData() const; + + enum JsonFormat { + Indented, + Compact + }; + + static JsonDocument fromJson(const std::string &json, JsonParseError *error = nullptr); + + std::string toJson(JsonFormat format = Indented) const; + + bool isEmpty() const; + bool isArray() const; + bool isObject() const; + + JsonObject object() const; + JsonArray array() const; + + void setObject(const JsonObject &object); + void setArray(const JsonArray &array); + + bool operator==(const JsonDocument &other) const; + bool operator!=(const JsonDocument &other) const { return !(*this == other); } + + bool isNull() const; + +private: + friend class JsonValue; + friend class Internal::Data; + friend class Internal::Parser; + + JsonDocument(Internal::Data *data); + + Internal::Data *d; +}; + +} // namespace Json + +#endif // JSONVALUE_H diff --git a/src/shared/json/json.pri b/src/shared/json/json.pri new file mode 100644 index 00000000000..db7ce19eee9 --- /dev/null +++ b/src/shared/json/json.pri @@ -0,0 +1,2 @@ +HEADERS += $$PWD/json.h +SOURCES += $$PWD/json.cpp diff --git a/src/shared/json/json.qbs b/src/shared/json/json.qbs new file mode 100644 index 00000000000..3587d307afc --- /dev/null +++ b/src/shared/json/json.qbs @@ -0,0 +1,15 @@ +import qbs + +StaticLibrary { + name: "qtcjson" + Depends { name: "cpp" } + cpp.cxxLanguageVersion: "c++11" + files: [ + "json.cpp", + "json.h", + ] + Export { + Depends { name: "cpp" } + cpp.includePaths: [product.sourceDirectory] + } +} diff --git a/src/src.qbs b/src/src.qbs index 5b8c62e332e..1953849d02e 100644 --- a/src/src.qbs +++ b/src/src.qbs @@ -45,6 +45,7 @@ Project { qbsBaseDir + "/src/plugins/plugins.qbs", qbsBaseDir + "/share/share.qbs", qbsBaseDir + "/src/app/apps.qbs", + project.sharedSourcesDir + "/json", ] } } diff --git a/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri b/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri index e0e49498e3a..73aa363a959 100644 --- a/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri +++ b/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri @@ -24,7 +24,8 @@ HEADERS += $$PWD/clangipcserver.h \ $$PWD/fixit.h \ $$PWD/diagnosticsetiterator.h \ $$PWD/clangfilesystemwatcher.h \ - $$PWD/translationunitalreadyexistsexception.h + $$PWD/translationunitalreadyexistsexception.h \ + $$PWD/commandlinearguments.h SOURCES += $$PWD/clangipcserver.cpp \ $$PWD/codecompleter.cpp \ @@ -49,4 +50,5 @@ SOURCES += $$PWD/clangipcserver.cpp \ $$PWD/sourcerange.cpp \ $$PWD/fixit.cpp \ $$PWD/clangfilesystemwatcher.cpp \ - $$PWD/translationunitalreadyexistsexception.cpp + $$PWD/translationunitalreadyexistsexception.cpp \ + $$PWD/commandlinearguments.cpp diff --git a/src/tools/clangbackend/ipcsource/commandlinearguments.cpp b/src/tools/clangbackend/ipcsource/commandlinearguments.cpp new file mode 100644 index 00000000000..41c80430be9 --- /dev/null +++ b/src/tools/clangbackend/ipcsource/commandlinearguments.cpp @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qt Creator. +** +** 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 http://www.qt.io/terms-conditions. For further information +** use the contact form at http://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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "commandlinearguments.h" + +#include + +namespace ClangBackEnd { + +CommandLineArguments::CommandLineArguments(const char *filePath, + const std::vector &projectPartArguments, + const Utf8StringVector &fileArguments, + bool addVerboseOption) +{ + const auto elementsToReserve = projectPartArguments.size() + + uint(fileArguments.size()) + + (addVerboseOption ? 1 : 0); + m_arguments.reserve(elementsToReserve); + + m_arguments = projectPartArguments; + for (const auto &argument : fileArguments) + m_arguments.push_back(argument.constData()); + if (addVerboseOption) + m_arguments.push_back("-v"); + m_arguments.push_back(filePath); +} + +const char * const *CommandLineArguments::data() const +{ + return m_arguments.data(); +} + +int CommandLineArguments::count() const +{ + return int(m_arguments.size()); +} + +const char *CommandLineArguments::at(int position) const +{ + return m_arguments.at(uint(position)); +} + +void CommandLineArguments::print() const +{ + using namespace std; + + cerr << "Arguments to libclang:"; + for (const auto &argument : m_arguments) + cerr << ' ' << argument; + cerr << endl; +} + +} // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/commandlinearguments.h b/src/tools/clangbackend/ipcsource/commandlinearguments.h new file mode 100644 index 00000000000..225b6407b30 --- /dev/null +++ b/src/tools/clangbackend/ipcsource/commandlinearguments.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qt Creator. +** +** 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 http://www.qt.io/terms-conditions. For further information +** use the contact form at http://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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef CLANGBACKEND_COMMANDLINEARGUMENTS_H +#define CLANGBACKEND_COMMANDLINEARGUMENTS_H + +#include + +#include + +namespace ClangBackEnd { + +class CommandLineArguments +{ +public: + CommandLineArguments(const char *filePath, + const std::vector &projectPartArguments, + const Utf8StringVector &fileArguments, + bool addVerboseOption); + + const char * const *data() const; + int count() const; + const char * at(int position) const; + + void print() const; + +private: + std::vector m_arguments; +}; + +} // namespace ClangBackEnd + +#endif // CLANGBACKEND_COMMANDLINEARGUMENTS_H diff --git a/src/tools/clangbackend/ipcsource/diagnostic.cpp b/src/tools/clangbackend/ipcsource/diagnostic.cpp index f0220e79581..e851a7c730e 100644 --- a/src/tools/clangbackend/ipcsource/diagnostic.cpp +++ b/src/tools/clangbackend/ipcsource/diagnostic.cpp @@ -106,13 +106,15 @@ DiagnosticSeverity Diagnostic::severity() const std::vector Diagnostic::ranges() const { std::vector ranges; - const uint rangesCount = clang_getDiagnosticNumRanges(cxDiagnostic); - ranges.reserve(rangesCount); - for (uint index = 0; index < rangesCount; ++index) - ranges.push_back(SourceRange(clang_getDiagnosticRange(cxDiagnostic, index))); + for (uint index = 0; index < rangesCount; ++index) { + const SourceRange sourceRange(clang_getDiagnosticRange(cxDiagnostic, index)); + + if (sourceRange.isValid()) + ranges.push_back(SourceRange(clang_getDiagnosticRange(cxDiagnostic, index))); + } return ranges; } diff --git a/src/tools/clangbackend/ipcsource/sourcerange.cpp b/src/tools/clangbackend/ipcsource/sourcerange.cpp index 4ea26ba1fed..7375988b2bd 100644 --- a/src/tools/clangbackend/ipcsource/sourcerange.cpp +++ b/src/tools/clangbackend/ipcsource/sourcerange.cpp @@ -39,6 +39,16 @@ SourceRange::SourceRange() { } +bool SourceRange::isNull() const +{ + return clang_Range_isNull(cxSourceRange); +} + +bool SourceRange::isValid() const +{ + return !isNull() && start().offset() < end().offset(); +} + SourceLocation SourceRange::start() const { return SourceLocation(clang_getRangeStart(cxSourceRange)); diff --git a/src/tools/clangbackend/ipcsource/sourcerange.h b/src/tools/clangbackend/ipcsource/sourcerange.h index cec9f6fa658..3c80e4e040d 100644 --- a/src/tools/clangbackend/ipcsource/sourcerange.h +++ b/src/tools/clangbackend/ipcsource/sourcerange.h @@ -44,6 +44,10 @@ class SourceRange public: SourceRange(); + + bool isNull() const; + bool isValid() const; + SourceLocation start() const; SourceLocation end() const; diff --git a/src/tools/clangbackend/ipcsource/translationunit.cpp b/src/tools/clangbackend/ipcsource/translationunit.cpp index 55e14bca55d..1f483244bf1 100644 --- a/src/tools/clangbackend/ipcsource/translationunit.cpp +++ b/src/tools/clangbackend/ipcsource/translationunit.cpp @@ -32,6 +32,7 @@ #include "clangstring.h" #include "codecompleter.h" +#include "commandlinearguments.h" #include "diagnosticset.h" #include "projectpart.h" #include "translationunitfilenotexitexception.h" @@ -50,6 +51,11 @@ static Q_LOGGING_CATEGORY(verboseLibLog, "qtc.clangbackend.verboselib"); +static bool isVerboseModeEnabled() +{ + return verboseLibLog().isDebugEnabled(); +} + namespace ClangBackEnd { class TranslationUnitData @@ -57,6 +63,7 @@ class TranslationUnitData public: TranslationUnitData(const Utf8String &filePath, const ProjectPart &projectPart, + const Utf8StringVector &fileArguments, TranslationUnits &translationUnits); ~TranslationUnitData(); @@ -65,6 +72,7 @@ public: time_point lastProjectPartChangeTimePoint; QSet dependedFilePaths; ProjectPart projectPart; + Utf8StringVector fileArguments; Utf8String filePath; CXTranslationUnit translationUnit = nullptr; CXIndex index = nullptr; @@ -75,10 +83,12 @@ public: TranslationUnitData::TranslationUnitData(const Utf8String &filePath, const ProjectPart &projectPart, + const Utf8StringVector &fileArguments, TranslationUnits &translationUnits) : translationUnits(translationUnits), lastProjectPartChangeTimePoint(std::chrono::steady_clock::now()), projectPart(projectPart), + fileArguments(fileArguments), filePath(filePath) { dependedFilePaths.insert(filePath); @@ -92,9 +102,13 @@ TranslationUnitData::~TranslationUnitData() TranslationUnit::TranslationUnit(const Utf8String &filePath, const ProjectPart &projectPart, + const Utf8StringVector &fileArguments, TranslationUnits &translationUnits, FileExistsCheck fileExistsCheck) - : d(std::make_shared(filePath, projectPart, translationUnits)) + : d(std::make_shared(filePath, + projectPart, + fileArguments, + translationUnits)) { if (fileExistsCheck == CheckIfFileExists) checkIfFileExists(); @@ -122,7 +136,7 @@ CXIndex TranslationUnit::index() const checkIfNull(); if (!d->index) { - const bool displayDiagnostics = verboseLibLog().isDebugEnabled(); + const bool displayDiagnostics = isVerboseModeEnabled(); d->index = clang_createIndex(1, displayDiagnostics); } @@ -261,10 +275,15 @@ void TranslationUnit::createTranslationUnitIfNeeded() const { if (!d->translationUnit) { d->translationUnit = CXTranslationUnit(); + + const auto args = commandLineArguments(); + if (isVerboseModeEnabled()) + args.print(); + CXErrorCode errorCode = clang_parseTranslationUnit2(index(), - d->filePath.constData(), - d->projectPart.cxArguments(), - d->projectPart.argumentCount(), + NULL, + args.data(), + args.count(), unsavedFiles().cxUnsavedFiles(), unsavedFiles().count(), defaultOptions(), @@ -274,7 +293,6 @@ void TranslationUnit::createTranslationUnitIfNeeded() const updateIncludeFilePaths(); - updateLastProjectPartChangeTimePoint(); } } @@ -338,6 +356,14 @@ void TranslationUnit::updateIncludeFilePaths() const d->translationUnits.addWatchedFiles(d->dependedFilePaths); } +CommandLineArguments TranslationUnit::commandLineArguments() const +{ + return CommandLineArguments(d->filePath.constData(), + d->projectPart.arguments(), + d->fileArguments, + isVerboseModeEnabled()); +} + uint TranslationUnit::defaultOptions() { return CXTranslationUnit_CacheCompletionResults diff --git a/src/tools/clangbackend/ipcsource/translationunit.h b/src/tools/clangbackend/ipcsource/translationunit.h index 2bd1f153a0a..0b22359d744 100644 --- a/src/tools/clangbackend/ipcsource/translationunit.h +++ b/src/tools/clangbackend/ipcsource/translationunit.h @@ -52,6 +52,7 @@ class ProjectPart; class DiagnosticSet; class FileContainer; class TranslationUnits; +class CommandLineArguments; using time_point = std::chrono::steady_clock::time_point; @@ -66,6 +67,7 @@ public: TranslationUnit() = default; TranslationUnit(const Utf8String &filePath, const ProjectPart &projectPart, + const Utf8StringVector &fileArguments, TranslationUnits &translationUnits, FileExistsCheck fileExistsCheck = CheckIfFileExists); ~TranslationUnit(); @@ -105,6 +107,8 @@ public: void setDirtyIfDependencyIsMet(const Utf8String &filePath); + CommandLineArguments commandLineArguments() const; + private: void checkIfNull() const; void checkIfFileExists() const; diff --git a/src/tools/clangbackend/ipcsource/translationunits.cpp b/src/tools/clangbackend/ipcsource/translationunits.cpp index b0790ac5738..c542b2d98ca 100644 --- a/src/tools/clangbackend/ipcsource/translationunits.cpp +++ b/src/tools/clangbackend/ipcsource/translationunits.cpp @@ -200,6 +200,7 @@ void TranslationUnits::createTranslationUnit(const FileContainer &fileContainer) if (findIterator == translationUnits_.end()) { translationUnits_.push_back(TranslationUnit(fileContainer.filePath(), projectParts.project(fileContainer.projectPartId()), + fileContainer.fileArguments(), *this, checkIfFileExists)); translationUnits_.back().setDocumentRevision(fileContainer.documentRevision()); diff --git a/src/tools/sdktool/sdktool.pro b/src/tools/sdktool/sdktool.pro index 72edc66b75a..2ec19a9b591 100644 --- a/src/tools/sdktool/sdktool.pro +++ b/src/tools/sdktool/sdktool.pro @@ -47,4 +47,5 @@ HEADERS += \ settings.h \ macx:DEFINES += "DATA_PATH=\"\\\".\\\"\"" -else:DEFINES += "DATA_PATH=\"\\\"../share/qtcreator\\\"\"" +else:win32:DEFINES += "DATA_PATH=\"\\\"../share/qtcreator\\\"\"" +else:DEFINES += "DATA_PATH=\"\\\"../../share/qtcreator\\\"\"" diff --git a/src/tools/sdktool/sdktool.qbs b/src/tools/sdktool/sdktool.qbs index 88be3dcd66e..be97b827502 100644 --- a/src/tools/sdktool/sdktool.qbs +++ b/src/tools/sdktool/sdktool.qbs @@ -8,7 +8,9 @@ QtcTool { Depends { name: "app_version_header" } cpp.defines: base.concat([qbs.targetOS.contains("osx") - ? 'DATA_PATH="."' : 'DATA_PATH="../share/qtcreator"']) + ? 'DATA_PATH="."' + : qbs.targetOS.contains("windows") ? 'DATA_PATH="../share/qtcreator"' + : 'DATA_PATH="../../share/qtcreator"']) files: [ "adddebuggeroperation.cpp", "adddebuggeroperation.h", diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro index 6284497e4c5..eabe10b3a19 100644 --- a/tests/auto/auto.pro +++ b/tests/auto/auto.pro @@ -13,6 +13,7 @@ SUBDIRS += \ profilewriter \ treeviewfind \ qtcprocess \ + json \ utils \ filesearch \ sdktool \ diff --git a/tests/auto/auto.qbs b/tests/auto/auto.qbs index 91c6e593f88..15465a61e6f 100644 --- a/tests/auto/auto.qbs +++ b/tests/auto/auto.qbs @@ -14,6 +14,7 @@ Project { "externaltool/externaltool.qbs", "filesearch/filesearch.qbs", "generichighlighter/generichighlighter.qbs", + "json/json.qbs", "profilewriter/profilewriter.qbs", "qml/qml.qbs", "qtcprocess/qtcprocess.qbs", diff --git a/tests/auto/cplusplus/checksymbols/tst_checksymbols.cpp b/tests/auto/cplusplus/checksymbols/tst_checksymbols.cpp index 57c9f338232..3b16861c8b1 100644 --- a/tests/auto/cplusplus/checksymbols/tst_checksymbols.cpp +++ b/tests/auto/cplusplus/checksymbols/tst_checksymbols.cpp @@ -47,6 +47,10 @@ Tests CheckSymbols, the "data provider" of the semantic highlighter. */ +// When adding tests, you may want to set this enum +// in order to print out all found uses. +enum { enableListing = 0 }; + using namespace CPlusPlus; using namespace CppTools; @@ -57,34 +61,27 @@ typedef SemanticHighlighter Highlighting; typedef QList UseList; Q_DECLARE_METATYPE(UseList) +#define CASE_STR(val) case Highlighting::val: return "Highlighting::" # val static QString useKindToString(UseKind useKind) { switch (useKind) { - case Highlighting::Unknown: - return QLatin1String("SemanticHighlighter::Unknown"); - case Highlighting::TypeUse: - return QLatin1String("SemanticHighlighter::TypeUse"); - case Highlighting::LocalUse: - return QLatin1String("SemanticHighlighter::LocalUse"); - case Highlighting::FieldUse: - return QLatin1String("SemanticHighlighter::FieldUse"); - case Highlighting::EnumerationUse: - return QLatin1String("SemanticHighlighter::EnumerationUse"); - case Highlighting::VirtualMethodUse: - return QLatin1String("SemanticHighlighter::VirtualMethodUse"); - case Highlighting::LabelUse: - return QLatin1String("SemanticHighlighter::LabelUse"); - case Highlighting::MacroUse: - return QLatin1String("SemanticHighlighter::MacroUse"); - case Highlighting::FunctionUse: - return QLatin1String("SemanticHighlighter::FunctionUse"); - case Highlighting::PseudoKeywordUse: - return QLatin1String("SemanticHighlighter::PseudoKeywordUse"); + CASE_STR(Unknown); + CASE_STR(TypeUse); + CASE_STR(LocalUse); + CASE_STR(FieldUse); + CASE_STR(EnumerationUse); + CASE_STR(VirtualMethodUse); + CASE_STR(LabelUse); + CASE_STR(MacroUse); + CASE_STR(FunctionUse); + CASE_STR(PseudoKeywordUse); + CASE_STR(StringUse); default: QTest::qFail("Unknown UseKind", __FILE__, __LINE__); return QLatin1String("Unknown UseKind"); } } +#undef CASE_STR // The following two functions are "enhancements" for QCOMPARE(). QT_BEGIN_NAMESPACE @@ -112,11 +109,11 @@ class BaseTestCase public: BaseTestCase(const QByteArray &source, const UseList &expectedUsesMacros = UseList()) { - // Write source to temprorary file + // Write source to temporary file const QString filePath = QDir::tempPath() + QLatin1String("/file.h"); Tests::TestCase::writeFile(filePath, source); - // Processs source + // Process source const Document::Ptr document = createDocument(filePath, source); QVERIFY(document); Snapshot snapshot; @@ -182,14 +179,21 @@ public: { const int resultCount = future.resultCount(); UseList actualUses; + QByteArray expectedInput; + if (enableListing) + expectedInput = _("\n") + _(8, ' ') + "<< (UseList()\n"; for (int i = 0; i < resultCount; ++i) { const Use use = future.resultAt(i); - // When adding tests, you may want to uncomment the - // following line in order to print out all found uses. - // qDebug() << QTest::toString(use); + if (enableListing) + expectedInput += _(12, ' ') + "<< " + _(QTest::toString(use)) + "\n"; actualUses.append(use); } + if (enableListing) { + expectedInput.chop(1); + expectedInput += ')'; + qDebug() << expectedInput; + } // Checks QVERIFY(resultCount > 0); QCOMPARE(resultCount, expectedUsesAll.count()); @@ -221,6 +225,8 @@ private slots: void test_checksymbols_infiniteLoop_data(); void test_checksymbols_infiniteLoop(); + void test_checksymbols_infiniteLoop_BUG15141(); + void test_parentOfBlock(); void findField(); @@ -1118,6 +1124,25 @@ void tst_CheckSymbols::test_checksymbols_infiniteLoop() TestCase::runCheckSymbols(document1, snapshot); } +void tst_CheckSymbols::test_checksymbols_infiniteLoop_BUG15141() +{ + QByteArray source = + "template \n" + "struct Base\n" + "{\n" + "};\n" + "\n" + "template\n" + "struct Derived :\n" + " Base<\n" + " typename Derived::type>::type,\n" + " typename Derived::type>::type\n" + " >::type\n" + "{};\n"; + + BaseTestCase tc(source); +} + void tst_CheckSymbols::test_parentOfBlock() { const QByteArray source = "void C::f()\n" diff --git a/tests/auto/debugger/README.txt b/tests/auto/debugger/README.txt index b5c151a786c..13396b9bb76 100644 --- a/tests/auto/debugger/README.txt +++ b/tests/auto/debugger/README.txt @@ -16,8 +16,11 @@ conditions by using environment variables as follows: "normal" standard library, and the "debug" version (this will add DEFINES += _GLIBCXX_DEBUG) to the .pro - (QTC_MSVC_ENV_BAT - to set up MSVC) + QTC_BOOST_INCLUDE_PATH_FOR_TEST - include path for boost libraries + only necessary if you have more than one version in different + paths installed or if a non-standard path has been used + (QTC_MSVC_ENV_BAT - to set up MSVC) The tests should be used for automated testing, but can also be used for dumper development and fixing. diff --git a/tests/auto/debugger/tst_dumpers.cpp b/tests/auto/debugger/tst_dumpers.cpp index fe31eb3b354..f55827bfccb 100644 --- a/tests/auto/debugger/tst_dumpers.cpp +++ b/tests/auto/debugger/tst_dumpers.cpp @@ -246,12 +246,13 @@ static QString toHex(const QString &str) struct Context { - Context() : qtVersion(0), gccVersion(0), clangVersion(0) {} + Context() : qtVersion(0), gccVersion(0), clangVersion(0), boostVersion(0) {} QByteArray nameSpace; int qtVersion; int gccVersion; int clangVersion; + int boostVersion; }; struct Name @@ -482,6 +483,7 @@ struct CheckBase mutable VersionBase gccVersionForCheck; mutable VersionBase clangVersionForCheck; mutable QtVersion qtVersionForCheck; + mutable BoostVersion boostVersionForCheck; mutable bool optionallyPresent; }; @@ -549,6 +551,12 @@ struct Check : CheckBase return *this; } + const Check &operator%(BoostVersion version) + { + boostVersionForCheck = version; + return *this; + } + QByteArray iname; Name expectedName; Value expectedValue; @@ -608,8 +616,14 @@ struct Cxx11Profile : public Profile struct BoostProfile : public Profile { BoostProfile() - : Profile("macx:INCLUDEPATH += /usr/local/include") - {} + : Profile(QByteArray()) + { + const QByteArray &boostIncPath = qgetenv("QTC_BOOST_INCLUDE_PATH_FOR_TEST"); + if (!boostIncPath.isEmpty()) + contents = QByteArray("INCLUDEPATH += ") + boostIncPath.constData(); + else + contents = "macx:INCLUDEPATH += /usr/local/include"; + } }; struct MacLibCppProfile : public Profile @@ -1182,6 +1196,11 @@ void tst_Dumpers::dumper() "\n#else" "\n int clangversion = 0;" "\n#endif" + "\n#ifdef BOOST_VERSION" + "\n int boostversion = BOOST_VERSION;" + "\n#else" + "\n int boostversion = 0;" + "\n#endif" "\n" + (data.useQHash ? "\n#if QT_VERSION >= 0x050000" "\nqt_qhash_seed.store(0);" @@ -1281,7 +1300,8 @@ void tst_Dumpers::dumper() "python from gdbbridge import *\n" "python theDumper.setupDumpers()\n" "run " + nograb + "\n" - "python theDumper.fetchVariables({'fancy':1,'forcens':1," + "python theDumper.fetchVariables({" + "'token':2,'fancy':1,'forcens':1,'sortstructs':1," "'autoderef':1,'dyntype':1,'passexceptions':1," "'expanded':[" + expandedq + "]})\n"; @@ -1312,7 +1332,9 @@ void tst_Dumpers::dumper() "sc sys.path.insert(1, '" + dumperDir + "')\n" "sc from lldbbridge import *\n" // "sc print(dir())\n" - "sc Tester('" + t->buildPath.toLatin1() + "/doit', [" + expandedq + "])\n" + "sc Tester('" + t->buildPath.toLatin1() + "/doit', {'fancy':1,'forcens':1," + "'autoderef':1,'dyntype':1,'passexceptions':1," + "'expanded':[" + expandedq + "]})\n" "quit\n"; fullLldb.write(cmds); @@ -1415,6 +1437,8 @@ void tst_Dumpers::dumper() context.gccVersion = child["value"].toInt(); else if (dummy.iname == "local.clangversion") context.clangVersion = child["value"].toInt(); + else if (dummy.iname == "local.boostversion") + context.boostVersion = child["value"].toInt(); else parseWatchData(dummy, child, &list); } @@ -2771,7 +2795,7 @@ void tst_Dumpers::dumper_data() "QSharedPointer ptr20(new QString(\"hallo\"));\n" "QSharedPointer ptr21 = ptr20;\n" "QSharedPointer ptr22 = ptr20;\n" - "unused(&ptr20, &ptr21, &ptr21);\n\n" + "unused(&ptr20, &ptr21, &ptr22);\n\n" "QSharedPointer ptr30(new int(43));\n" "QWeakPointer ptr31(ptr30);\n" @@ -4113,6 +4137,7 @@ void tst_Dumpers::dumper_data() << Data("#include \n", "std::set s0;\n" + "unused(&s0);\n\n" "std::set s1;\n" "s1.insert(11);\n" @@ -5251,7 +5276,7 @@ void tst_Dumpers::dumper_data() "l.push_back(p(15, 65));\n" "l.push_back(p(16, 66));\n") + BoostProfile() - + Check("l", "<4 items>", "boost::container::list>") + + Check("l", "<4 items>", Pattern("boost::container::list.*>")) + Check("l.2.second", "65", "double"); @@ -5269,16 +5294,14 @@ void tst_Dumpers::dumper_data() "s2.insert(\"def\");\n") + BoostProfile() - + BoostVersion(1 * 100000 + 54 * 100) // FIXME: Not checked - + GdbVersion(70600) // Crude replacement instead - + Check("s1", "<2 items>", "boost::unordered::unordered_set") - + Check("s1.0", "[0]", "22", "int") - + Check("s1.1", "[1]", "11", "int") + + Check("s1", "<2 items>", "boost::unordered::unordered_set") % BoostVersion(1 * 100000 + 54 * 100) + + Check("s1.0", "[0]", "22", "int") % BoostVersion(1 * 100000 + 54 * 100) + + Check("s1.1", "[1]", "11", "int") % BoostVersion(1 * 100000 + 54 * 100) - + Check("s2", "<2 items>", "boost::unordered::unordered_set") - + Check("s2.0", "[0]", "\"def\"", "std::string") - + Check("s2.1", "[1]", "\"abc\"", "std::string"); + + Check("s2", "<2 items>", "boost::unordered::unordered_set") % BoostVersion(1 * 100000 + 54 * 100) + + Check("s2.0", "[0]", "\"def\"", "std::string") % BoostVersion(1 * 100000 + 54 * 100) + + Check("s2.1", "[1]", "\"abc\"", "std::string") % BoostVersion(1 * 100000 + 54 * 100); // // This tests qdump__KRBase in share/qtcreator/debugger/qttypes.py which uses diff --git a/tests/auto/debugger/tst_offsets.cpp b/tests/auto/debugger/tst_offsets.cpp index 1b507cbb0f3..62eb3b740b6 100644 --- a/tests/auto/debugger/tst_offsets.cpp +++ b/tests/auto/debugger/tst_offsets.cpp @@ -83,7 +83,17 @@ void tst_offsets::offsets_data() QFilePrivate *p = 0; QTestData &data = QTest::newRow("QFilePrivate::fileName") << int((char *)&p->fileName - (char *)p); - if (qtVersion >= 0x50500) + if (qtVersion >= 0x50600) +#ifdef Q_OS_WIN +# ifdef Q_CC_MSVC + data << 176 << 248; +# else // MinGW + data << 164 << 248; +# endif +#else + data << 168 << 248; +#endif + else if (qtVersion >= 0x50500) #ifdef Q_OS_WIN # ifdef Q_CC_MSVC data << 176 << 248; diff --git a/tests/auto/debugger/tst_simplifytypes.cpp b/tests/auto/debugger/tst_simplifytypes.cpp index 2251e8ca5ea..7b3fe3f92f3 100644 --- a/tests/auto/debugger/tst_simplifytypes.cpp +++ b/tests/auto/debugger/tst_simplifytypes.cpp @@ -40,6 +40,8 @@ const char *description[] = { "g++_stdstring", "g++_stdwstring", + "g++_5stdstring", + "g++_5stdwstring", "g++_stringmap", "g++_wstringmap", "g++_stringlist", @@ -75,6 +77,8 @@ const char *input[] = // g++ "std::string", "std::wstring", +"std::__cxx11::basic_string, std::allocator >", +"std::__cxx11::basic_string, std::allocator >", "std::map, std::allocator >, std::basic_string, std::allocator >, std::less, std::allocator > >, std::allocator, std::allocator > const, std::basic_string, std::allocator > > > >", "std::map, std::allocator >, std::basic_string, std::allocator >, std::less, std::allocator > >, std::allocator, std::allocator > const, std::basic_string, std::allocator > > > >", "std::list, std::allocator >, std::allocator, std::allocator > > >", @@ -116,6 +120,8 @@ const char *output[] = // Gcc "std::string", "std::wstring", + "std::string", + "std::wstring", "std::map", "std::map", "std::list", diff --git a/tests/auto/json/bom.json b/tests/auto/json/bom.json new file mode 100644 index 00000000000..d1e8d90e280 --- /dev/null +++ b/tests/auto/json/bom.json @@ -0,0 +1,3 @@ +{ + "info-version": "1.0" +} diff --git a/tests/auto/json/json.pro b/tests/auto/json/json.pro new file mode 100644 index 00000000000..c9f7b7d447e --- /dev/null +++ b/tests/auto/json/json.pro @@ -0,0 +1,13 @@ +TARGET = tst_json +QT = core testlib +CONFIG -= app_bundle +CONFIG += testcase c++11 + +TESTDATA += test.json test.bjson test3.json test2.json bom.json + +INCLUDEPATH += ../../../src/shared/json + +DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII + +include(../../../src/shared/json/json.pri) +SOURCES += tst_json.cpp diff --git a/tests/auto/json/json.qbs b/tests/auto/json/json.qbs new file mode 100644 index 00000000000..de9a22fcf2a --- /dev/null +++ b/tests/auto/json/json.qbs @@ -0,0 +1,22 @@ +import qbs + +QtcAutotest { + name: "json autotest" + Depends { name: "bundle" } + Depends { name: "qtcjson" } + + bundle.isBundle: false + + Group { + name: "test data" + files: [ + "bom.json", + "test.bjson", + "test.json", + "test2.json", + "test3.json", + ] + } + + files: ["tst_json.cpp"] +} diff --git a/tests/auto/json/test.bjson b/tests/auto/json/test.bjson new file mode 100644 index 00000000000..ae12e5e446b Binary files /dev/null and b/tests/auto/json/test.bjson differ diff --git a/tests/auto/json/test.json b/tests/auto/json/test.json new file mode 100644 index 00000000000..330756894a6 --- /dev/null +++ b/tests/auto/json/test.json @@ -0,0 +1,66 @@ +[ + "JSON Test Pattern pass1", + {"object with 1 member":["array with 1 element"]}, + {}, + [], + -42, + true, + false, + null, + { + "integer": 1234567890, + "real": -9876.543210, + "e": 0.123456789e-12, + "E": 1.234567890E+34, + "": 23456789012E66, + "zero": 0, + "one": 1, + "space": " ", + "quote": "\"", + "backslash": "\\", + "controls": "\b\f\n\r\t", + "slash": "/ & \/", + "alpha": "abcdefghijklmnopqrstuvwxyz", + "ALPHA": "ABCDEFGHIJKLMNOPQRSTUVWXYZ", + "digit": "0123456789", + "0123456789": "digit", + "special": "`1~!@#$%^&*()_+-={\':[,]}|;.?", + "hex": "\u0123\u4567\u89AB\uCDEF\uabcd\uef4A", + "true": true, + "false": false, + "null": null, + "array":[ ], + "object":{ }, + "address": "50 St. James Street", + "url": "nix", + "comment": "// /* */": " ", + " s p a c e d " :[1,2 , 3 + +, + +4 , 5 , 6 ,7 ],"compact":[1,2,3,4,5,6,7], + "jsontext": "{\"object with 1 member\":[\"array with 1 element\"]}", + "quotes": "" \u0022 %22 0x22 034 "", + "\/\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:\',./<>?" : "A key can be any string" + }, + 0.5 ,98.6 +, +99.44 +, + +1066, +1e1, +0.1e1, +1e-1, +1e00, +2e+00, +2e-00, +"rosebud", +{"foo": "bar"}, +{"classification":{"relevancyScore":1000,"searchUrl":{"value":"nix"}},"products":{"priceSet":{"minPrice":{"value":"$0.01","integral":1},"maxPrice":{"value":"$4,833.99","integral":483399}},"product":[{"type":"PRODUCT","title":"Silicone c","description":"Elite Hori","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":310711221747712.000000,"priceSet":{"minPrice":{"value":"$1.56","integral":156},"maxPrice":{"value":"$29.99","integral":2999},"stores":14},"id":1968262863,"categoryId":8515},{"type":"PRODUCT","title":"Nonslip Ch","description":"Specificat","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":175580930637824.000000,"priceSet":{"minPrice":{"value":"$0.45","integral":45},"maxPrice":{"value":"$194.95","integral":19495},"stores":34},"id":2534935499,"categoryId":8515},{"type":"PRODUCT","title":"Plastic Ca","description":"Descriptio","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":132488642953216.000000,"priceSet":{"minPrice":{"value":"$0.99","integral":99},"maxPrice":{"value":"$303.68","integral":30368},"stores":33},"id":2305624670,"categoryId":8515},{"type":"PRODUCT","title":"Protective","description":"Made of hi","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":108614681362432.000000,"priceSet":{"minPrice":{"value":"$1.70","integral":170},"maxPrice":{"value":"$99.99","integral":9999},"stores":11},"id":2120981405,"categoryId":8515},{"type":"PRODUCT","title":"P® 4","description":"Do more th","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":96203484168192.000000,"priceSet":{"minPrice":{"value":"$2.49","integral":249},"maxPrice":{"value":"$79.95","integral":7995},"stores":16},"id":2203798762,"categoryId":8515},{"type":"PRODUCT","title":"Case Refle","description":"NCAA iPhon","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":84727583211520.000000,"priceSet":{"minPrice":{"value":"$0.69","integral":69},"maxPrice":{"value":"$75.52","integral":7552},"stores":59},"id":1114627445,"categoryId":8515},{"type":"PRODUCT","title":"Infuse Pro","description":"Protect an","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":80831066406912.000000,"priceSet":{"minPrice":{"value":"$0.59","integral":59},"maxPrice":{"value":"$79.00","integral":7900},"stores":24},"id":2557462717,"categoryId":8515},{"type":"PRODUCT","title":"Dragonfly ","description":"d","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":70900229603328.000000,"priceSet":{"minPrice":{"value":"$1.05","integral":105},"maxPrice":{"value":"$94.49","integral":9449},"stores":30},"id":2442061740,"categoryId":8515},{"type":"PRODUCT","title":"Pho","description":"Snap on Ap","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":65194915004416.000000,"priceSet":{"minPrice":{"value":"$0.01","integral":1},"maxPrice":{"value":"$414.99","integral":41499},"stores":39},"id":2004746863,"categoryId":8515},{"type":"PRODUCT","title":"Otterbox i","description":"Your iPhon","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":61515478597632.000000,"priceSet":{"minPrice":{"value":"$3.28","integral":328},"maxPrice":{"value":"$110.65","integral":11065},"stores":25},"id":2584611575,"categoryId":8515}],"includedResults":10,"totalResults":2000}}, +{"classification":{"relevancyScore":1000,"searchUrl":{"value":"nix"}},"products":{"priceSet":{"minPrice":{"value":"$0.01","integral":1},"maxPrice":{"value":"$4,833.99","integral":483399}},"product":[{"type":"PRODUCT","title":"Silicone c","description":"Elite Hori","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":310711221747712.000000,"priceSet":{"minPrice":{"value":"$1.56","integral":156},"maxPrice":{"value":"$29.99","integral":2999},"stores":14},"id":1968262863,"categoryId":8515},{"type":"PRODUCT","title":"Nonslip Ch","description":"Specificat","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":175580930637824.000000,"priceSet":{"minPrice":{"value":"$0.45","integral":45},"maxPrice":{"value":"$194.95","integral":19495},"stores":34},"id":2534935499,"categoryId":8515},{"type":"PRODUCT","title":"Plastic Ca","description":"Descriptio","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":132488642953216.000000,"priceSet":{"minPrice":{"value":"$0.99","integral":99},"maxPrice":{"value":"$303.68","integral":30368},"stores":33},"id":2305624670,"categoryId":8515},{"type":"PRODUCT","title":"Protective","description":"Made of hi","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":108614681362432.000000,"priceSet":{"minPrice":{"value":"$1.70","integral":170},"maxPrice":{"value":"$99.99","integral":9999},"stores":11},"id":2120981405,"categoryId":8515},{"type":"PRODUCT","title":"P® 4","description":"Do more th","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":96203484168192.000000,"priceSet":{"minPrice":{"value":"$2.49","integral":249},"maxPrice":{"value":"$79.95","integral":7995},"stores":16},"id":2203798762,"categoryId":8515},{"type":"PRODUCT","title":"Case Refle","description":"NCAA iPhon","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":84727583211520.000000,"priceSet":{"minPrice":{"value":"$0.69","integral":69},"maxPrice":{"value":"$75.52","integral":7552},"stores":59},"id":1114627445,"categoryId":8515},{"type":"PRODUCT","title":"Infuse Pro","description":"Protect an","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":80831066406912.000000,"priceSet":{"minPrice":{"value":"$0.59","integral":59},"maxPrice":{"value":"$79.00","integral":7900},"stores":24},"id":2557462717,"categoryId":8515},{"type":"PRODUCT","title":"Dragonfly ","description":"d","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":70900229603328.000000,"priceSet":{"minPrice":{"value":"$1.05","integral":105},"maxPrice":{"value":"$94.49","integral":9449},"stores":30},"id":2442061740,"categoryId":8515},{"type":"PRODUCT","title":"Pho","description":"Snap on Ap","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":65194915004416.000000,"priceSet":{"minPrice":{"value":"$0.01","integral":1},"maxPrice":{"value":"$414.99","integral":41499},"stores":39},"id":2004746863,"categoryId":8515},{"type":"PRODUCT","title":"Otterbox i","description":"Your iPhon","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":61515478597632.000000,"priceSet":{"minPrice":{"value":"$3.28","integral":328},"maxPrice":{"value":"$110.65","integral":11065},"stores":25},"id":2584611575,"categoryId":8515}],"includedResults":10,"totalResults":2000}}, +{"classification":{"relevancyScore":1000,"searchUrl":{"value":"nix"}},"products":{"priceSet":{"minPrice":{"value":"$0.01","integral":1},"maxPrice":{"value":"$4,833.99","integral":483399}},"product":[{"type":"PRODUCT","title":"Silicone c","description":"Elite Hori","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":310711221747712.000000,"priceSet":{"minPrice":{"value":"$1.56","integral":156},"maxPrice":{"value":"$29.99","integral":2999},"stores":14},"id":1968262863,"categoryId":8515},{"type":"PRODUCT","title":"Nonslip Ch","description":"Specificat","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":175580930637824.000000,"priceSet":{"minPrice":{"value":"$0.45","integral":45},"maxPrice":{"value":"$194.95","integral":19495},"stores":34},"id":2534935499,"categoryId":8515},{"type":"PRODUCT","title":"Plastic Ca","description":"Descriptio","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":132488642953216.000000,"priceSet":{"minPrice":{"value":"$0.99","integral":99},"maxPrice":{"value":"$303.68","integral":30368},"stores":33},"id":2305624670,"categoryId":8515},{"type":"PRODUCT","title":"Protective","description":"Made of hi","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":108614681362432.000000,"priceSet":{"minPrice":{"value":"$1.70","integral":170},"maxPrice":{"value":"$99.99","integral":9999},"stores":11},"id":2120981405,"categoryId":8515},{"type":"PRODUCT","title":"P® 4","description":"Do more th","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":96203484168192.000000,"priceSet":{"minPrice":{"value":"$2.49","integral":249},"maxPrice":{"value":"$79.95","integral":7995},"stores":16},"id":2203798762,"categoryId":8515},{"type":"PRODUCT","title":"Case Refle","description":"NCAA iPhon","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":84727583211520.000000,"priceSet":{"minPrice":{"value":"$0.69","integral":69},"maxPrice":{"value":"$75.52","integral":7552},"stores":59},"id":1114627445,"categoryId":8515},{"type":"PRODUCT","title":"Infuse Pro","description":"Protect an","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":80831066406912.000000,"priceSet":{"minPrice":{"value":"$0.59","integral":59},"maxPrice":{"value":"$79.00","integral":7900},"stores":24},"id":2557462717,"categoryId":8515},{"type":"PRODUCT","title":"Dragonfly ","description":"d","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":70900229603328.000000,"priceSet":{"minPrice":{"value":"$1.05","integral":105},"maxPrice":{"value":"$94.49","integral":9449},"stores":30},"id":2442061740,"categoryId":8515},{"type":"PRODUCT","title":"Pho","description":"Snap on Ap","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":65194915004416.000000,"priceSet":{"minPrice":{"value":"$0.01","integral":1},"maxPrice":{"value":"$414.99","integral":41499},"stores":39},"id":2004746863,"categoryId":8515},{"type":"PRODUCT","title":"Otterbox i","description":"Your iPhon","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":61515478597632.000000,"priceSet":{"minPrice":{"value":"$3.28","integral":328},"maxPrice":{"value":"$110.65","integral":11065},"stores":25},"id":2584611575,"categoryId":8515}],"includedResults":10,"totalResults":2000}}, +{"classification":{"relevancyScore":1000,"searchUrl":{"value":"nix"}},"products":{"priceSet":{"minPrice":{"value":"$0.01","integral":1},"maxPrice":{"value":"$4,833.99","integral":483399}},"product":[{"type":"PRODUCT","title":"Silicone c","description":"Elite Hori","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":310711221747712.000000,"priceSet":{"minPrice":{"value":"$1.56","integral":156},"maxPrice":{"value":"$29.99","integral":2999},"stores":14},"id":1968262863,"categoryId":8515},{"type":"PRODUCT","title":"Nonslip Ch","description":"Specificat","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":175580930637824.000000,"priceSet":{"minPrice":{"value":"$0.45","integral":45},"maxPrice":{"value":"$194.95","integral":19495},"stores":34},"id":2534935499,"categoryId":8515},{"type":"PRODUCT","title":"Plastic Ca","description":"Descriptio","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":132488642953216.000000,"priceSet":{"minPrice":{"value":"$0.99","integral":99},"maxPrice":{"value":"$303.68","integral":30368},"stores":33},"id":2305624670,"categoryId":8515},{"type":"PRODUCT","title":"Protective","description":"Made of hi","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":108614681362432.000000,"priceSet":{"minPrice":{"value":"$1.70","integral":170},"maxPrice":{"value":"$99.99","integral":9999},"stores":11},"id":2120981405,"categoryId":8515},{"type":"PRODUCT","title":"P® 4","description":"Do more th","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":96203484168192.000000,"priceSet":{"minPrice":{"value":"$2.49","integral":249},"maxPrice":{"value":"$79.95","integral":7995},"stores":16},"id":2203798762,"categoryId":8515},{"type":"PRODUCT","title":"Case Refle","description":"NCAA iPhon","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":84727583211520.000000,"priceSet":{"minPrice":{"value":"$0.69","integral":69},"maxPrice":{"value":"$75.52","integral":7552},"stores":59},"id":1114627445,"categoryId":8515},{"type":"PRODUCT","title":"Infuse Pro","description":"Protect an","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":80831066406912.000000,"priceSet":{"minPrice":{"value":"$0.59","integral":59},"maxPrice":{"value":"$79.00","integral":7900},"stores":24},"id":2557462717,"categoryId":8515},{"type":"PRODUCT","title":"Dragonfly ","description":"d","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":70900229603328.000000,"priceSet":{"minPrice":{"value":"$1.05","integral":105},"maxPrice":{"value":"$94.49","integral":9449},"stores":30},"id":2442061740,"categoryId":8515},{"type":"PRODUCT","title":"Pho","description":"Snap on Ap","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":65194915004416.000000,"priceSet":{"minPrice":{"value":"$0.01","integral":1},"maxPrice":{"value":"$414.99","integral":41499},"stores":39},"id":2004746863,"categoryId":8515},{"type":"PRODUCT","title":"Otterbox i","description":"Your iPhon","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":61515478597632.000000,"priceSet":{"minPrice":{"value":"$3.28","integral":328},"maxPrice":{"value":"$110.65","integral":11065},"stores":25},"id":2584611575,"categoryId":8515}],"includedResults":10,"totalResults":2000}} +] + diff --git a/tests/auto/json/test2.json b/tests/auto/json/test2.json new file mode 100644 index 00000000000..303f879b626 --- /dev/null +++ b/tests/auto/json/test2.json @@ -0,0 +1 @@ +{ "foo": ["ab"] } diff --git a/tests/auto/json/test3.json b/tests/auto/json/test3.json new file mode 100644 index 00000000000..48cb29a47f1 --- /dev/null +++ b/tests/auto/json/test3.json @@ -0,0 +1,15 @@ +{ + "firstName": "John", + "lastName" : "Smith", + "age" : 25, + "address" : { + "streetAddress": "21 2nd Street", + "city" : "New York", + "state" : "NY", + "postalCode" : "10021" + }, + "phoneNumber": [ + { "type" : "home", "number": "212 555-1234" }, + { "type" : "fax", "number": "646 555-4567" } + ] +} diff --git a/tests/auto/json/tst_json.cpp b/tests/auto/json/tst_json.cpp new file mode 100644 index 00000000000..0de0c104404 --- /dev/null +++ b/tests/auto/json/tst_json.cpp @@ -0,0 +1,2525 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include +#include +#include + +#include + +#define INVALID_UNICODE "\xCE\xBA\xE1" +#define UNICODE_NON_CHARACTER "\xEF\xBF\xBF" +#define UNICODE_DJE "\320\202" // Character from the Serbian Cyrillic alphabet + +using namespace Json; + +Q_DECLARE_METATYPE(Json::JsonArray) +Q_DECLARE_METATYPE(Json::JsonObject) + +bool contains(const JsonObject::Keys &keys, const std::string &key) +{ + return std::find(keys.begin(), keys.end(), key) != keys.end(); +} + +class tst_Json: public QObject +{ + Q_OBJECT + +public: + tst_Json(QObject *parent = 0); + +private slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + + void testValueSimple(); + void testNumbers(); + void testNumbers_2(); + void testNumbers_3(); + + void testObjectSimple(); + void testObjectSmallKeys(); + void testArraySimple(); + void testValueObject(); + void testValueArray(); + void testObjectNested(); + void testArrayNested(); + void testArrayNestedEmpty(); + void testObjectNestedEmpty(); + + void testValueRef(); + void testObjectIteration(); + void testArrayIteration(); + + void testObjectFind(); + + void testDocument(); + + void nullValues(); + void nullArrays(); + void nullObject(); + void constNullObject(); + + void keySorting(); + + void undefinedValues(); + + void toJson(); + void toJsonSillyNumericValues(); + void toJsonLargeNumericValues(); + void fromJson(); + void fromJsonErrors(); + void fromBinary(); + void toAndFromBinary_data(); + void toAndFromBinary(); + void parseNumbers(); + void parseStrings(); + void parseDuplicateKeys(); + void testParser(); + + void compactArray(); + void compactObject(); + + void validation(); + + void assignToDocument(); + + void testDuplicateKeys(); + void testCompaction(); + void testCompactionError(); + + void parseUnicodeEscapes(); + + void assignObjects(); + void assignArrays(); + + void testTrailingComma(); + void testDetachBug(); + void testJsonValueRefDefault(); + + void valueEquals(); + void objectEquals_data(); + void objectEquals(); + void arrayEquals_data(); + void arrayEquals(); + + void bom(); + void nesting(); + + void longStrings(); + + void arrayInitializerList(); + void objectInitializerList(); + + void unicodeKeys(); + void garbageAtEnd(); + + void removeNonLatinKey(); +private: + QString testDataDir; +}; + +tst_Json::tst_Json(QObject *parent) : QObject(parent) +{ +} + +void tst_Json::initTestCase() +{ + testDataDir = QFileInfo(QFINDTESTDATA("test.json")).absolutePath(); + if (testDataDir.isEmpty()) + testDataDir = QCoreApplication::applicationDirPath(); +} + +void tst_Json::cleanupTestCase() +{ +} + +void tst_Json::init() +{ +} + +void tst_Json::cleanup() +{ +} + +void tst_Json::testValueSimple() +{ + JsonObject object; + object.insert("number", 999.); + JsonArray array; + for (int i = 0; i < 10; ++i) + array.append((double)i); + + JsonValue value(true); + QCOMPARE(value.type(), JsonValue::Bool); + QCOMPARE(value.toDouble(), 0.); + QCOMPARE(value.toString(), std::string()); + QCOMPARE(value.toBool(), true); + QCOMPARE(value.toObject(), JsonObject()); + QCOMPARE(value.toArray(), JsonArray()); + QCOMPARE(value.toDouble(99.), 99.); + QCOMPARE(value.toString("test"), std::string("test")); + QCOMPARE(value.toObject(object), object); + QCOMPARE(value.toArray(array), array); + + value = 999.; + QCOMPARE(value.type(), JsonValue::Double); + QCOMPARE(value.toDouble(), 999.); + QCOMPARE(value.toString(), std::string()); + QCOMPARE(value.toBool(), false); + QCOMPARE(value.toBool(true), true); + QCOMPARE(value.toObject(), JsonObject()); + QCOMPARE(value.toArray(), JsonArray()); + + value = "test"; + QCOMPARE(value.toDouble(), 0.); + QCOMPARE(value.toString(), std::string("test")); + QCOMPARE(value.toBool(), false); + QCOMPARE(value.toObject(), JsonObject()); + QCOMPARE(value.toArray(), JsonArray()); +} + +void tst_Json::testNumbers() +{ + { + int numbers[] = { + 0, + -1, + 1, + (1<<26), + (1<<27), + (1<<28), + -(1<<26), + -(1<<27), + -(1<<28), + (1<<26) - 1, + (1<<27) - 1, + (1<<28) - 1, + -((1<<26) - 1), + -((1<<27) - 1), + -((1<<28) - 1) + }; + int n = sizeof(numbers)/sizeof(int); + + JsonArray array; + for (int i = 0; i < n; ++i) + array.append((double)numbers[i]); + + std::string serialized = JsonDocument(array).toJson(); + JsonDocument json = JsonDocument::fromJson(serialized); + JsonArray array2 = json.array(); + + QCOMPARE(array.size(), array2.size()); + for (int i = 0; i < array.size(); ++i) { + QCOMPARE(array.at(i).type(), JsonValue::Double); + QCOMPARE(array.at(i).toDouble(), (double)numbers[i]); + QCOMPARE(array2.at(i).type(), JsonValue::Double); + QCOMPARE(array2.at(i).toDouble(), (double)numbers[i]); + } + } + + { + int64_t numbers[] = { + 0, + -1, + 1, + (1ll<<54), + (1ll<<55), + (1ll<<56), + -(1ll<<54), + -(1ll<<55), + -(1ll<<56), + (1ll<<54) - 1, + (1ll<<55) - 1, + (1ll<<56) - 1, + -((1ll<<54) - 1), + -((1ll<<55) - 1), + -((1ll<<56) - 1) + }; + int n = sizeof(numbers)/sizeof(int64_t); + + JsonArray array; + for (int i = 0; i < n; ++i) + array.append((double)numbers[i]); + + std::string serialized = JsonDocument(array).toJson(); + JsonDocument json = JsonDocument::fromJson(serialized); + JsonArray array2 = json.array(); + + QCOMPARE(array.size(), array2.size()); + for (int i = 0; i < array.size(); ++i) { + QCOMPARE(array.at(i).type(), JsonValue::Double); + QCOMPARE(array.at(i).toDouble(), (double)numbers[i]); + QCOMPARE(array2.at(i).type(), JsonValue::Double); + QCOMPARE(array2.at(i).toDouble(), (double)numbers[i]); + } + } + + { + double numbers[] = { + 0, + -1, + 1, + double(1ll<<54), + double(1ll<<55), + double(1ll<<56), + double(-(1ll<<54)), + double(-(1ll<<55)), + double(-(1ll<<56)), + double((1ll<<54) - 1), + double((1ll<<55) - 1), + double((1ll<<56) - 1), + double(-((1ll<<54) - 1)), + double(-((1ll<<55) - 1)), + double(-((1ll<<56) - 1)), + 1.1, + 0.1, + -0.1, + -1.1, + 1e200, + -1e200 + }; + int n = sizeof(numbers)/sizeof(double); + + JsonArray array; + for (int i = 0; i < n; ++i) + array.append(numbers[i]); + + std::string serialized = JsonDocument(array).toJson(); + JsonDocument json = JsonDocument::fromJson(serialized); + JsonArray array2 = json.array(); + + QCOMPARE(array.size(), array2.size()); + for (int i = 0; i < array.size(); ++i) { + QCOMPARE(array.at(i).type(), JsonValue::Double); + QCOMPARE(array.at(i).toDouble(), numbers[i]); + QCOMPARE(array2.at(i).type(), JsonValue::Double); + QCOMPARE(array2.at(i).toDouble(), numbers[i]); + } + } + +} + +void tst_Json::testNumbers_2() +{ + // test cases from TC39 test suite for ECMAScript + // http://hg.ecmascript.org/tests/test262/file/d067d2f0ca30/test/suite/ch08/8.5/8.5.1.js + + // Fill an array with 2 to the power of (0 ... -1075) + double value = 1; + double floatValues[1076], floatValues_1[1076]; + JsonObject jObject; + for (int power = 0; power <= 1075; power++) { + floatValues[power] = value; + jObject.insert(std::to_string(power), JsonValue(floatValues[power])); + // Use basic math operations for testing, which are required to support 'gradual underflow' rather + // than Math.pow etc..., which are defined as 'implementation dependent'. + value = value * 0.5; + } + + JsonDocument jDocument1(jObject); + std::string ba(jDocument1.toJson()); + + JsonDocument jDocument2(JsonDocument::fromJson(ba)); + for (int power = 0; power <= 1075; power++) { + floatValues_1[power] = jDocument2.object().value(std::to_string(power)).toDouble(); +#ifdef Q_OS_QNX + if (power >= 970) + QEXPECT_FAIL("", "See QTBUG-37066", Abort); +#endif + QVERIFY2(floatValues[power] == floatValues_1[power], + QString::fromLatin1("floatValues[%1] != floatValues_1[%1]").arg(power).toLatin1()); + } + + // The last value is below min denorm and should round to 0, everything else should contain a value + QVERIFY2(floatValues_1[1075] == 0, "Value after min denorm should round to 0"); + + // Validate the last actual value is min denorm + QVERIFY2(floatValues_1[1074] == 4.9406564584124654417656879286822e-324, + QString::fromLatin1("Min denorm value is incorrect: %1").arg(floatValues_1[1074]).toLatin1()); + + // Validate that every value is half the value before it up to 1 + for (int index = 1074; index > 0; index--) { + QVERIFY2(floatValues_1[index] != 0, + QString::fromLatin1("2**- %1 should not be 0").arg(index).toLatin1()); + + QVERIFY2(floatValues_1[index - 1] == (floatValues_1[index] * 2), + QString::fromLatin1("Value should be double adjacent value at index %1").arg(index).toLatin1()); + } +} + +void tst_Json::testNumbers_3() +{ + // test case from QTBUG-31926 + double d1 = 1.123451234512345; + double d2 = 1.123451234512346; + + JsonObject jObject; + jObject.insert("d1", JsonValue(d1)); + jObject.insert("d2", JsonValue(d2)); + JsonDocument jDocument1(jObject); + std::string ba(jDocument1.toJson()); + + JsonDocument jDocument2(JsonDocument::fromJson(ba)); + + double d1_1(jDocument2.object().value("d1").toDouble()); + double d2_1(jDocument2.object().value("d2").toDouble()); + QVERIFY(d1_1 != d2_1); +} + +void tst_Json::testObjectSimple() +{ + JsonObject object; + object.insert("number", 999.); + QCOMPARE(object.value("number").type(), JsonValue::Double); + QCOMPARE(object.value("number").toDouble(), 999.); + object.insert("string", std::string("test")); + QCOMPARE(object.value("string").type(), JsonValue::String); + QCOMPARE(object.value("string").toString(), std::string("test")); + object.insert("boolean", true); + QCOMPARE(object.value("boolean").toBool(), true); + + JsonObject::Keys keys = object.keys(); + QVERIFY2(contains(keys, "number"), "key number not found"); + QVERIFY2(contains(keys, "string"), "key string not found"); + QVERIFY2(contains(keys, "boolean"), "key boolean not found"); + + // if we put a JsonValue into the JsonObject and retrieve + // it, it should be identical. + JsonValue value("foo"); + object.insert("value", value); + QCOMPARE(object.value("value"), value); + + int size = object.size(); + object.remove("boolean"); + QCOMPARE(object.size(), size - 1); + QVERIFY2(!object.contains("boolean"), "key boolean should have been removed"); + + JsonValue taken = object.take("value"); + QCOMPARE(taken, value); + QVERIFY2(!object.contains("value"), "key value should have been removed"); + + std::string before = object.value("string").toString(); + object.insert("string", std::string("foo")); + QVERIFY2(object.value("string").toString() != before, "value should have been updated"); + + size = object.size(); + JsonObject subobject; + subobject.insert("number", 42); + subobject.insert("string", "foobar"); + object.insert("subobject", subobject); + QCOMPARE(object.size(), size+1); + JsonValue subvalue = object.take("subobject"); + QCOMPARE(object.size(), size); + QCOMPARE(subvalue.toObject(), subobject); + // make object detach by modifying it many times + for (int i = 0; i < 64; ++i) + object.insert("string", "bar"); + QCOMPARE(object.size(), size); + QCOMPARE(subvalue.toObject(), subobject); +} + +void tst_Json::testObjectSmallKeys() +{ + JsonObject data1; + data1.insert("1", 123.); + QVERIFY(data1.contains("1")); + QCOMPARE(data1.value("1").toDouble(), (double)123); + data1.insert("12", 133.); + QCOMPARE(data1.value("12").toDouble(), (double)133); + QVERIFY(data1.contains("12")); + data1.insert("123", 323.); + QCOMPARE(data1.value("12").toDouble(), (double)133); + QVERIFY(data1.contains("123")); + QCOMPARE(data1.value("123").type(), JsonValue::Double); + QCOMPARE(data1.value("123").toDouble(), (double)323); +} + +void tst_Json::testArraySimple() +{ + JsonArray array; + array.append(999.); + array.append(std::string("test")); + array.append(true); + + JsonValue val = array.at(0); + QCOMPARE(array.at(0).toDouble(), 999.); + QCOMPARE(array.at(1).toString(), std::string("test")); + QCOMPARE(array.at(2).toBool(), true); + QCOMPARE(array.size(), 3); + + // if we put a JsonValue into the JsonArray and retrieve + // it, it should be identical. + JsonValue value("foo"); + array.append(value); + QCOMPARE(array.at(3), value); + + int size = array.size(); + array.removeAt(2); + --size; + QCOMPARE(array.size(), size); + + JsonValue taken = array.takeAt(0); + --size; + QCOMPARE(taken.toDouble(), 999.); + QCOMPARE(array.size(), size); + + // check whether null values work + array.append(JsonValue()); + ++size; + QCOMPARE(array.size(), size); + QCOMPARE(array.last().type(), JsonValue::Null); + QCOMPARE(array.last(), JsonValue()); + + QCOMPARE(array.first().type(), JsonValue::String); + QCOMPARE(array.first(), JsonValue("test")); + + array.prepend(false); + QCOMPARE(array.first().type(), JsonValue::Bool); + QCOMPARE(array.first(), JsonValue(false)); + + QCOMPARE(array.at(-1), JsonValue(JsonValue::Undefined)); + QCOMPARE(array.at(array.size()), JsonValue(JsonValue::Undefined)); + + array.replace(0, -555.); + QCOMPARE(array.first().type(), JsonValue::Double); + QCOMPARE(array.first(), JsonValue(-555.)); + QCOMPARE(array.at(1).type(), JsonValue::String); + QCOMPARE(array.at(1), JsonValue("test")); +} + +void tst_Json::testValueObject() +{ + JsonObject object; + object.insert("number", 999.); + object.insert("string", "test"); + object.insert("boolean", true); + + JsonValue value(object); + + // if we don't modify the original JsonObject, toObject() + // on the JsonValue should return the same object (non-detached). + QCOMPARE(value.toObject(), object); + + // if we modify the original object, it should detach + object.insert("test", JsonValue("test")); + QVERIFY2(value.toObject() != object, "object should have detached"); +} + +void tst_Json::testValueArray() +{ + JsonArray array; + array.append(999.); + array.append("test"); + array.append(true); + + JsonValue value(array); + + // if we don't modify the original JsonArray, toArray() + // on the JsonValue should return the same object (non-detached). + QCOMPARE(value.toArray(), array); + + // if we modify the original array, it should detach + array.append("test"); + QVERIFY2(value.toArray() != array, "array should have detached"); +} + +void tst_Json::testObjectNested() +{ + JsonObject inner, outer; + inner.insert("number", 999.); + outer.insert("nested", inner); + + // if we don't modify the original JsonObject, value() + // should return the same object (non-detached). + JsonObject value = outer.value("nested").toObject(); + QCOMPARE(value, inner); + QCOMPARE(value.value("number").toDouble(), 999.); + + // if we modify the original object, it should detach and not + // affect the nested object + inner.insert("number", 555.); + value = outer.value("nested").toObject(); + QVERIFY2(inner.value("number").toDouble() != value.value("number").toDouble(), + "object should have detached"); + + // array in object + JsonArray array; + array.append(123.); + array.append(456.); + outer.insert("array", array); + QCOMPARE(outer.value("array").toArray(), array); + QCOMPARE(outer.value("array").toArray().at(1).toDouble(), 456.); + + // two deep objects + JsonObject twoDeep; + twoDeep.insert("boolean", true); + inner.insert("nested", twoDeep); + outer.insert("nested", inner); + QCOMPARE(outer.value("nested").toObject().value("nested").toObject(), twoDeep); + QCOMPARE(outer.value("nested").toObject().value("nested").toObject().value("boolean").toBool(), + true); +} + +void tst_Json::testArrayNested() +{ + JsonArray inner, outer; + inner.append(999.); + outer.append(inner); + + // if we don't modify the original JsonArray, value() + // should return the same array (non-detached). + JsonArray value = outer.at(0).toArray(); + QCOMPARE(value, inner); + QCOMPARE(value.at(0).toDouble(), 999.); + + // if we modify the original array, it should detach and not + // affect the nested array + inner.append(555.); + value = outer.at(0).toArray(); + QVERIFY2(inner.size() != value.size(), "array should have detached"); + + // objects in arrays + JsonObject object; + object.insert("boolean", true); + outer.append(object); + QCOMPARE(outer.last().toObject(), object); + QCOMPARE(outer.last().toObject().value("boolean").toBool(), true); + + // two deep arrays + JsonArray twoDeep; + twoDeep.append(JsonValue("nested")); + inner.append(twoDeep); + outer.append(inner); + QCOMPARE(outer.last().toArray().last().toArray(), twoDeep); + QCOMPARE(outer.last().toArray().last().toArray().at(0).toString(), std::string("nested")); +} + +void tst_Json::testArrayNestedEmpty() +{ + JsonObject object; + JsonArray inner; + object.insert("inner", inner); + JsonValue val = object.value("inner"); + JsonArray value = object.value("inner").toArray(); + QCOMPARE(value.size(), 0); + QCOMPARE(value, inner); + QCOMPARE(value.size(), 0); + object.insert("count", 0.); + QCOMPARE(object.value("inner").toArray().size(), 0); + QVERIFY(object.value("inner").toArray().isEmpty()); + JsonDocument(object).toBinaryData(); + QCOMPARE(object.value("inner").toArray().size(), 0); +} + +void tst_Json::testObjectNestedEmpty() +{ + JsonObject object; + JsonObject inner; + JsonObject inner2; + object.insert("inner", inner); + object.insert("inner2", inner2); + JsonObject value = object.value("inner").toObject(); + QCOMPARE(value.size(), 0); + QCOMPARE(value, inner); + QCOMPARE(value.size(), 0); + object.insert("count", 0.); + QCOMPARE(object.value("inner").toObject().size(), 0); + QCOMPARE(object.value("inner").type(), JsonValue::Object); + JsonDocument(object).toBinaryData(); + QVERIFY(object.value("inner").toObject().isEmpty()); + QVERIFY(object.value("inner2").toObject().isEmpty()); + JsonDocument doc = JsonDocument::fromBinaryData(JsonDocument(object).toBinaryData()); + QVERIFY(!doc.isNull()); + JsonObject reconstituted(doc.object()); + QCOMPARE(reconstituted.value("inner").toObject().size(), 0); + QCOMPARE(reconstituted.value("inner").type(), JsonValue::Object); + QCOMPARE(reconstituted.value("inner2").type(), JsonValue::Object); +} + +void tst_Json::testValueRef() +{ + JsonArray array; + array.append(1.); + array.append(2.); + array.append(3.); + array.append(4); + array.append(4.1); + array[1] = false; + + QCOMPARE(array.size(), 5); + QCOMPARE(array.at(0).toDouble(), 1.); + QCOMPARE(array.at(2).toDouble(), 3.); + QCOMPARE(array.at(3).toInt(), 4); + QCOMPARE(array.at(4).toInt(), 0); + QCOMPARE(array.at(1).type(), JsonValue::Bool); + QCOMPARE(array.at(1).toBool(), false); + + JsonObject object; + object["key"] = true; + QCOMPARE(object.size(), 1); + object.insert("null", JsonValue()); + QCOMPARE(object.value("null"), JsonValue()); + object["null"] = 100.; + QCOMPARE(object.value("null").type(), JsonValue::Double); + JsonValue val = object["null"]; + QCOMPARE(val.toDouble(), 100.); + QCOMPARE(object.size(), 2); + + array[1] = array[2] = object["key"] = 42; + QCOMPARE(array[1], array[2]); + QCOMPARE(array[2], object["key"]); + QCOMPARE(object.value("key"), JsonValue(42)); +} + +void tst_Json::testObjectIteration() +{ + JsonObject object; + + for (JsonObject::iterator it = object.begin(); it != object.end(); ++it) + QVERIFY(false); + + const std::string property = "kkk"; + object.insert(property, 11); + object.take(property); + for (JsonObject::iterator it = object.begin(); it != object.end(); ++it) + QVERIFY(false); + + for (int i = 0; i < 10; ++i) + object[std::to_string(i)] = (double)i; + + QCOMPARE(object.size(), 10); + + QCOMPARE(object.begin()->toDouble(), object.constBegin()->toDouble()); + + for (JsonObject::iterator it = object.begin(); it != object.end(); ++it) { + JsonValue value = it.value(); + QCOMPARE((double)atoi(it.key().data()), value.toDouble()); + } + + { + JsonObject object2 = object; + QCOMPARE(object, object2); + + JsonValue val = *object2.begin(); + object2.erase(object2.begin()); + QCOMPARE(object.size(), 10); + QCOMPARE(object2.size(), 9); + + for (JsonObject::const_iterator it = object2.constBegin(); it != object2.constEnd(); ++it) { + JsonValue value = it.value(); + QVERIFY(it.value() != val); + QCOMPARE((double)atoi(it.key().data()), value.toDouble()); + } + } + + { + JsonObject object2 = object; + QCOMPARE(object, object2); + + JsonObject::iterator it = object2.find(std::to_string(5)); + object2.erase(it); + QCOMPARE(object.size(), 10); + QCOMPARE(object2.size(), 9); + } + + { + JsonObject::iterator it = object.begin(); + it += 5; + QCOMPARE(JsonValue(it.value()).toDouble(), 5.); + it -= 3; + QCOMPARE(JsonValue(it.value()).toDouble(), 2.); + JsonObject::iterator it2 = it + 5; + QCOMPARE(JsonValue(it2.value()).toDouble(), 7.); + it2 = it - 1; + QCOMPARE(JsonValue(it2.value()).toDouble(), 1.); + } + + { + JsonObject::const_iterator it = object.constBegin(); + it += 5; + QCOMPARE(JsonValue(it.value()).toDouble(), 5.); + it -= 3; + QCOMPARE(JsonValue(it.value()).toDouble(), 2.); + JsonObject::const_iterator it2 = it + 5; + QCOMPARE(JsonValue(it2.value()).toDouble(), 7.); + it2 = it - 1; + QCOMPARE(JsonValue(it2.value()).toDouble(), 1.); + } + + JsonObject::iterator it = object.begin(); + while (!object.isEmpty()) + it = object.erase(it); + QCOMPARE(object.size() , 0); + QCOMPARE(it, object.end()); +} + +void tst_Json::testArrayIteration() +{ + JsonArray array; + for (int i = 0; i < 10; ++i) + array.append(i); + + QCOMPARE(array.size(), 10); + + int i = 0; + for (JsonArray::iterator it = array.begin(); it != array.end(); ++it, ++i) { + JsonValue value = (*it); + QCOMPARE((double)i, value.toDouble()); + } + + QCOMPARE(array.begin()->toDouble(), array.constBegin()->toDouble()); + + { + JsonArray array2 = array; + QCOMPARE(array, array2); + + JsonValue val = *array2.begin(); + array2.erase(array2.begin()); + QCOMPARE(array.size(), 10); + QCOMPARE(array2.size(), 9); + + i = 1; + for (JsonArray::const_iterator it = array2.constBegin(); it != array2.constEnd(); ++it, ++i) { + JsonValue value = (*it); + QCOMPARE((double)i, value.toDouble()); + } + } + + { + JsonArray::iterator it = array.begin(); + it += 5; + QCOMPARE(JsonValue((*it)).toDouble(), 5.); + it -= 3; + QCOMPARE(JsonValue((*it)).toDouble(), 2.); + JsonArray::iterator it2 = it + 5; + QCOMPARE(JsonValue(*it2).toDouble(), 7.); + it2 = it - 1; + QCOMPARE(JsonValue(*it2).toDouble(), 1.); + } + + { + JsonArray::const_iterator it = array.constBegin(); + it += 5; + QCOMPARE(JsonValue((*it)).toDouble(), 5.); + it -= 3; + QCOMPARE(JsonValue((*it)).toDouble(), 2.); + JsonArray::const_iterator it2 = it + 5; + QCOMPARE(JsonValue(*it2).toDouble(), 7.); + it2 = it - 1; + QCOMPARE(JsonValue(*it2).toDouble(), 1.); + } + + JsonArray::iterator it = array.begin(); + while (!array.isEmpty()) + it = array.erase(it); + QCOMPARE(array.size() , 0); + QCOMPARE(it, array.end()); +} + +void tst_Json::testObjectFind() +{ + JsonObject object; + for (int i = 0; i < 10; ++i) + object[std::to_string(i)] = i; + + QCOMPARE(object.size(), 10); + + JsonObject::iterator it = object.find("1"); + QCOMPARE((*it).toDouble(), 1.); + it = object.find("11"); + QCOMPARE((*it).type(), JsonValue::Undefined); + QCOMPARE(it, object.end()); + + JsonObject::const_iterator cit = object.constFind("1"); + QCOMPARE((*cit).toDouble(), 1.); + cit = object.constFind("11"); + QCOMPARE((*it).type(), JsonValue::Undefined); + QCOMPARE(it, object.end()); +} + +void tst_Json::testDocument() +{ + JsonDocument doc; + QCOMPARE(doc.isEmpty(), true); + QCOMPARE(doc.isArray(), false); + QCOMPARE(doc.isObject(), false); + + JsonObject object; + doc.setObject(object); + QCOMPARE(doc.isEmpty(), false); + QCOMPARE(doc.isArray(), false); + QCOMPARE(doc.isObject(), true); + + object.insert("Key", "Value"); + doc.setObject(object); + QCOMPARE(doc.isEmpty(), false); + QCOMPARE(doc.isArray(), false); + QCOMPARE(doc.isObject(), true); + QCOMPARE(doc.object(), object); + QCOMPARE(doc.array(), JsonArray()); + + doc = JsonDocument(); + QCOMPARE(doc.isEmpty(), true); + QCOMPARE(doc.isArray(), false); + QCOMPARE(doc.isObject(), false); + + JsonArray array; + doc.setArray(array); + QCOMPARE(doc.isEmpty(), false); + QCOMPARE(doc.isArray(), true); + QCOMPARE(doc.isObject(), false); + + array.append("Value"); + doc.setArray(array); + QCOMPARE(doc.isEmpty(), false); + QCOMPARE(doc.isArray(), true); + QCOMPARE(doc.isObject(), false); + QCOMPARE(doc.array(), array); + QCOMPARE(doc.object(), JsonObject()); + + JsonObject outer; + outer.insert("outerKey", 22); + JsonObject inner; + inner.insert("innerKey", 42); + outer.insert("innter", inner); + JsonArray innerArray; + innerArray.append(23); + outer.insert("innterArray", innerArray); + + JsonDocument doc2(outer.value("innter").toObject()); + QVERIFY(doc2.object().contains("innerKey")); + QCOMPARE(doc2.object().value("innerKey"), JsonValue(42)); + + JsonDocument doc3; + doc3.setObject(outer.value("innter").toObject()); + QCOMPARE(doc3.isArray(), false); + QCOMPARE(doc3.isObject(), true); + QVERIFY(doc3.object().contains("innerKey")); + QCOMPARE(doc3.object().value("innerKey"), JsonValue(42)); + + JsonDocument doc4(outer.value("innterArray").toArray()); + QCOMPARE(doc4.isArray(), true); + QCOMPARE(doc4.isObject(), false); + QCOMPARE(doc4.array().size(), 1); + QCOMPARE(doc4.array().at(0), JsonValue(23)); + + JsonDocument doc5; + doc5.setArray(outer.value("innterArray").toArray()); + QCOMPARE(doc5.isArray(), true); + QCOMPARE(doc5.isObject(), false); + QCOMPARE(doc5.array().size(), 1); + QCOMPARE(doc5.array().at(0), JsonValue(23)); +} + +void tst_Json::nullValues() +{ + JsonArray array; + array.append(JsonValue()); + + QCOMPARE(array.size(), 1); + QCOMPARE(array.at(0), JsonValue()); + + JsonObject object; + object.insert("key", JsonValue()); + QCOMPARE(object.contains("key"), true); + QCOMPARE(object.size(), 1); + QCOMPARE(object.value("key"), JsonValue()); +} + +void tst_Json::nullArrays() +{ + JsonArray nullArray; + JsonArray nonNull; + nonNull.append("bar"); + + QCOMPARE(nullArray, JsonArray()); + QVERIFY(nullArray != nonNull); + QVERIFY(nonNull != nullArray); + + QCOMPARE(nullArray.size(), 0); + QCOMPARE(nullArray.takeAt(0), JsonValue(JsonValue::Undefined)); + QCOMPARE(nullArray.first(), JsonValue(JsonValue::Undefined)); + QCOMPARE(nullArray.last(), JsonValue(JsonValue::Undefined)); + nullArray.removeAt(0); + nullArray.removeAt(-1); + + nullArray.append("bar"); + nullArray.removeAt(0); + + QCOMPARE(nullArray.size(), 0); + QCOMPARE(nullArray.takeAt(0), JsonValue(JsonValue::Undefined)); + QCOMPARE(nullArray.first(), JsonValue(JsonValue::Undefined)); + QCOMPARE(nullArray.last(), JsonValue(JsonValue::Undefined)); + nullArray.removeAt(0); + nullArray.removeAt(-1); +} + +void tst_Json::nullObject() +{ + JsonObject nullObject; + JsonObject nonNull; + nonNull.insert("foo", "bar"); + + QCOMPARE(nullObject, JsonObject()); + QVERIFY(nullObject != nonNull); + QVERIFY(nonNull != nullObject); + + QCOMPARE(nullObject.size(), 0); + QCOMPARE(nullObject.keys(), JsonObject::Keys()); + nullObject.remove("foo"); + QCOMPARE(nullObject, JsonObject()); + QCOMPARE(nullObject.take("foo"), JsonValue(JsonValue::Undefined)); + QCOMPARE(nullObject.contains("foo"), false); + + nullObject.insert("foo", "bar"); + nullObject.remove("foo"); + + QCOMPARE(nullObject.size(), 0); + QCOMPARE(nullObject.keys(), JsonObject::Keys()); + nullObject.remove("foo"); + QCOMPARE(nullObject, JsonObject()); + QCOMPARE(nullObject.take("foo"), JsonValue(JsonValue::Undefined)); + QCOMPARE(nullObject.contains("foo"), false); +} + +void tst_Json::constNullObject() +{ + const JsonObject nullObject; + JsonObject nonNull; + nonNull.insert("foo", "bar"); + + QCOMPARE(nullObject, JsonObject()); + QVERIFY(nullObject != nonNull); + QVERIFY(nonNull != nullObject); + + QCOMPARE(nullObject.size(), 0); + QCOMPARE(nullObject.keys(), JsonObject::Keys()); + QCOMPARE(nullObject, JsonObject()); + QCOMPARE(nullObject.contains("foo"), false); + QCOMPARE(nullObject["foo"], JsonValue(JsonValue::Undefined)); +} + +void tst_Json::keySorting() +{ + const char *json = "{ \"B\": true, \"A\": false }"; + JsonDocument doc = JsonDocument::fromJson(json); + + QCOMPARE(doc.isObject(), true); + + JsonObject o = doc.object(); + QCOMPARE(o.size(), 2); + JsonObject::const_iterator it = o.constBegin(); + QCOMPARE(it.key(), std::string("A")); + ++it; + QCOMPARE(it.key(), std::string("B")); + + JsonObject::Keys keys; + keys.push_back("A"); + keys.push_back("B"); + QCOMPARE(o.keys(), keys); +} + +void tst_Json::undefinedValues() +{ + JsonObject object; + object.insert("Key", JsonValue(JsonValue::Undefined)); + QCOMPARE(object.size(), 0); + + object.insert("Key", "Value"); + QCOMPARE(object.size(), 1); + QCOMPARE(object.value("Key").type(), JsonValue::String); + QCOMPARE(object.value("foo").type(), JsonValue::Undefined); + object.insert("Key", JsonValue(JsonValue::Undefined)); + QCOMPARE(object.size(), 0); + QCOMPARE(object.value("Key").type(), JsonValue::Undefined); + + JsonArray array; + array.append(JsonValue(JsonValue::Undefined)); + QCOMPARE(array.size(), 1); + QCOMPARE(array.at(0).type(), JsonValue::Null); + + QCOMPARE(array.at(1).type(), JsonValue::Undefined); + QCOMPARE(array.at(-1).type(), JsonValue::Undefined); +} + +void tst_Json::toJson() +{ + // Test JsonDocument::Indented format + { + JsonObject object; + object.insert("\\Key\n", "Value"); + object.insert("null", JsonValue()); + JsonArray array; + array.append(true); + array.append(999.); + array.append("string"); + array.append(JsonValue()); + array.append("\\\a\n\r\b\tabcABC\""); + object.insert("Array", array); + + std::string json = JsonDocument(object).toJson(); + + std::string expected = + "{\n" + " \"Array\": [\n" + " true,\n" + " 999,\n" + " \"string\",\n" + " null,\n" + " \"\\\\\\u0007\\n\\r\\b\\tabcABC\\\"\"\n" + " ],\n" + " \"\\\\Key\\n\": \"Value\",\n" + " \"null\": null\n" + "}\n"; + QCOMPARE(json, expected); + + JsonDocument doc; + doc.setObject(object); + json = doc.toJson(); + QCOMPARE(json, expected); + + doc.setArray(array); + json = doc.toJson(); + expected = + "[\n" + " true,\n" + " 999,\n" + " \"string\",\n" + " null,\n" + " \"\\\\\\u0007\\n\\r\\b\\tabcABC\\\"\"\n" + "]\n"; + QCOMPARE(json, expected); + } + + // Test JsonDocument::Compact format + { + JsonObject object; + object.insert("\\Key\n", "Value"); + object.insert("null", JsonValue()); + JsonArray array; + array.append(true); + array.append(999.); + array.append("string"); + array.append(JsonValue()); + array.append("\\\a\n\r\b\tabcABC\""); + object.insert("Array", array); + + std::string json = JsonDocument(object).toJson(JsonDocument::Compact); + std::string expected = + "{\"Array\":[true,999,\"string\",null,\"\\\\\\u0007\\n\\r\\b\\tabcABC\\\"\"],\"\\\\Key\\n\":\"Value\",\"null\":null}"; + QCOMPARE(json, expected); + + JsonDocument doc; + doc.setObject(object); + json = doc.toJson(JsonDocument::Compact); + QCOMPARE(json, expected); + + doc.setArray(array); + json = doc.toJson(JsonDocument::Compact); + expected = "[true,999,\"string\",null,\"\\\\\\u0007\\n\\r\\b\\tabcABC\\\"\"]"; + QCOMPARE(json, expected); + } +} + +void tst_Json::toJsonSillyNumericValues() +{ + JsonObject object; + JsonArray array; + array.append(JsonValue(std::numeric_limits::infinity())); // encode to: null + array.append(JsonValue(-std::numeric_limits::infinity())); // encode to: null + array.append(JsonValue(std::numeric_limits::quiet_NaN())); // encode to: null + object.insert("Array", array); + + std::string json = JsonDocument(object).toJson(); + + std::string expected = + "{\n" + " \"Array\": [\n" + " null,\n" + " null,\n" + " null\n" + " ]\n" + "}\n"; + + QCOMPARE(json, expected); + + JsonDocument doc; + doc.setObject(object); + json = doc.toJson(); + QCOMPARE(json, expected); +} + +void tst_Json::toJsonLargeNumericValues() +{ + JsonObject object; + JsonArray array; + array.append(JsonValue(1.234567)); // actual precision bug in Qt 5.0.0 + array.append(JsonValue(1.7976931348623157e+308)); // JS Number.MAX_VALUE + array.append(JsonValue(5e-324)); // JS Number.MIN_VALUE + array.append(JsonValue(std::numeric_limits::min())); + array.append(JsonValue(std::numeric_limits::max())); + array.append(JsonValue(std::numeric_limits::epsilon())); + array.append(JsonValue(std::numeric_limits::denorm_min())); + array.append(JsonValue(0.0)); + array.append(JsonValue(-std::numeric_limits::min())); + array.append(JsonValue(-std::numeric_limits::max())); + array.append(JsonValue(-std::numeric_limits::epsilon())); + array.append(JsonValue(-std::numeric_limits::denorm_min())); + array.append(JsonValue(-0.0)); + array.append(JsonValue(int64_t(9007199254740992LL))); // JS Number max integer + array.append(JsonValue(int64_t(-9007199254740992LL))); // JS Number min integer + object.insert("Array", array); + + std::string json = JsonDocument(object).toJson(); + + std::string expected = + "{\n" + " \"Array\": [\n" + " 1.234567,\n" + " 1.7976931348623157e+308,\n" + // ((4.9406564584124654e-324 == 5e-324) == true) + // I can only think JavaScript has a special formatter to + // emit this value for this IEEE754 bit pattern. + " 4.9406564584124654e-324,\n" + " 2.2250738585072014e-308,\n" + " 1.7976931348623157e+308,\n" + " 2.2204460492503131e-16,\n" + " 4.9406564584124654e-324,\n" + " 0,\n" + " -2.2250738585072014e-308,\n" + " -1.7976931348623157e+308,\n" + " -2.2204460492503131e-16,\n" + " -4.9406564584124654e-324,\n" + " 0,\n" + " 9007199254740992,\n" + " -9007199254740992\n" + " ]\n" + "}\n"; + + QCOMPARE(json, expected); + + JsonDocument doc; + doc.setObject(object); + json = doc.toJson(); + QCOMPARE(json, expected); +} + +void tst_Json::fromJson() +{ + { + std::string json = "[\n true\n]\n"; + JsonDocument doc = JsonDocument::fromJson(json); + QVERIFY(!doc.isEmpty()); + QCOMPARE(doc.isArray(), true); + QCOMPARE(doc.isObject(), false); + JsonArray array = doc.array(); + QCOMPARE(array.size(), 1); + QCOMPARE(array.at(0).type(), JsonValue::Bool); + QCOMPARE(array.at(0).toBool(), true); + QCOMPARE(doc.toJson(), json); + } + { + //regression test: test if unicode_control_characters are correctly decoded + std::string json = "[\n \"" UNICODE_NON_CHARACTER "\"\n]\n"; + JsonDocument doc = JsonDocument::fromJson(json); + QVERIFY(!doc.isEmpty()); + QCOMPARE(doc.isArray(), true); + QCOMPARE(doc.isObject(), false); + JsonArray array = doc.array(); + QCOMPARE(array.size(), 1); + QCOMPARE(array.at(0).type(), JsonValue::String); + QCOMPARE(array.at(0).toString(), std::string(UNICODE_NON_CHARACTER)); + QCOMPARE(doc.toJson(), json); + } + { + std::string json = "[]"; + JsonDocument doc = JsonDocument::fromJson(json); + QVERIFY(!doc.isEmpty()); + QCOMPARE(doc.isArray(), true); + QCOMPARE(doc.isObject(), false); + JsonArray array = doc.array(); + QCOMPARE(array.size(), 0); + } + { + std::string json = "{}"; + JsonDocument doc = JsonDocument::fromJson(json); + QVERIFY(!doc.isEmpty()); + QCOMPARE(doc.isArray(), false); + QCOMPARE(doc.isObject(), true); + JsonObject object = doc.object(); + QCOMPARE(object.size(), 0); + } + { + std::string json = "{\n \"Key\": true\n}\n"; + JsonDocument doc = JsonDocument::fromJson(json); + QVERIFY(!doc.isEmpty()); + QCOMPARE(doc.isArray(), false); + QCOMPARE(doc.isObject(), true); + JsonObject object = doc.object(); + QCOMPARE(object.size(), 1); + QCOMPARE(object.value("Key"), JsonValue(true)); + QCOMPARE(doc.toJson(), json); + } + { + std::string json = "[ null, true, false, \"Foo\", 1, [], {} ]"; + JsonDocument doc = JsonDocument::fromJson(json); + QVERIFY(!doc.isEmpty()); + QCOMPARE(doc.isArray(), true); + QCOMPARE(doc.isObject(), false); + JsonArray array = doc.array(); + QCOMPARE(array.size(), 7); + QCOMPARE(array.at(0).type(), JsonValue::Null); + QCOMPARE(array.at(1).type(), JsonValue::Bool); + QCOMPARE(array.at(1).toBool(), true); + QCOMPARE(array.at(2).type(), JsonValue::Bool); + QCOMPARE(array.at(2).toBool(), false); + QCOMPARE(array.at(3).type(), JsonValue::String); + QCOMPARE(array.at(3).toString(), std::string("Foo")); + QCOMPARE(array.at(4).type(), JsonValue::Double); + QCOMPARE(array.at(4).toDouble(), 1.); + QCOMPARE(array.at(5).type(), JsonValue::Array); + QCOMPARE(array.at(5).toArray().size(), 0); + QCOMPARE(array.at(6).type(), JsonValue::Object); + QCOMPARE(array.at(6).toObject().size(), 0); + } + { + std::string json = "{ \"0\": null, \"1\": true, \"2\": false, \"3\": \"Foo\", \"4\": 1, \"5\": [], \"6\": {} }"; + JsonDocument doc = JsonDocument::fromJson(json); + QVERIFY(!doc.isEmpty()); + QCOMPARE(doc.isArray(), false); + QCOMPARE(doc.isObject(), true); + JsonObject object = doc.object(); + QCOMPARE(object.size(), 7); + QCOMPARE(object.value("0").type(), JsonValue::Null); + QCOMPARE(object.value("1").type(), JsonValue::Bool); + QCOMPARE(object.value("1").toBool(), true); + QCOMPARE(object.value("2").type(), JsonValue::Bool); + QCOMPARE(object.value("2").toBool(), false); + QCOMPARE(object.value("3").type(), JsonValue::String); + QCOMPARE(object.value("3").toString(), std::string("Foo")); + QCOMPARE(object.value("4").type(), JsonValue::Double); + QCOMPARE(object.value("4").toDouble(), 1.); + QCOMPARE(object.value("5").type(), JsonValue::Array); + QCOMPARE(object.value("5").toArray().size(), 0); + QCOMPARE(object.value("6").type(), JsonValue::Object); + QCOMPARE(object.value("6").toObject().size(), 0); + } + { + std::string compactJson = "{\"Array\": [true,999,\"string\",null,\"\\\\\\u0007\\n\\r\\b\\tabcABC\\\"\"],\"\\\\Key\\n\": \"Value\",\"null\": null}"; + JsonDocument doc = JsonDocument::fromJson(compactJson); + QVERIFY(!doc.isEmpty()); + QCOMPARE(doc.isArray(), false); + QCOMPARE(doc.isObject(), true); + JsonObject object = doc.object(); + QCOMPARE(object.size(), 3); + QCOMPARE(object.value("\\Key\n").isString(), true); + QCOMPARE(object.value("\\Key\n").toString(), std::string("Value")); + QCOMPARE(object.value("null").isNull(), true); + QCOMPARE(object.value("Array").isArray(), true); + JsonArray array = object.value("Array").toArray(); + QCOMPARE(array.size(), 5); + QCOMPARE(array.at(0).isBool(), true); + QCOMPARE(array.at(0).toBool(), true); + QCOMPARE(array.at(1).isDouble(), true); + QCOMPARE(array.at(1).toDouble(), 999.); + QCOMPARE(array.at(2).isString(), true); + QCOMPARE(array.at(2).toString(), std::string("string")); + QCOMPARE(array.at(3).isNull(), true); + QCOMPARE(array.at(4).isString(), true); + QCOMPARE(array.at(4).toString(), std::string("\\\a\n\r\b\tabcABC\"")); + } +} + +void tst_Json::fromJsonErrors() +{ + { + JsonParseError error; + std::string json = "{\n \n\n"; + JsonDocument doc = JsonDocument::fromJson(json, &error); + QVERIFY(doc.isEmpty()); + QCOMPARE(error.error, JsonParseError::UnterminatedObject); + QCOMPARE(error.offset, 8); + } + { + JsonParseError error; + std::string json = "{\n \"key\" 10\n"; + JsonDocument doc = JsonDocument::fromJson(json, &error); + QVERIFY(doc.isEmpty()); + QCOMPARE(error.error, JsonParseError::MissingNameSeparator); + QCOMPARE(error.offset, 13); + } + { + JsonParseError error; + std::string json = "[\n \n\n"; + JsonDocument doc = JsonDocument::fromJson(json, &error); + QVERIFY(doc.isEmpty()); + QCOMPARE(error.error, JsonParseError::UnterminatedArray); + QCOMPARE(error.offset, 8); + } + { + JsonParseError error; + std::string json = "[\n 1, true\n\n"; + JsonDocument doc = JsonDocument::fromJson(json, &error); + QVERIFY(doc.isEmpty()); + QCOMPARE(error.error, JsonParseError::UnterminatedArray); + QCOMPARE(error.offset, 14); + } + { + JsonParseError error; + std::string json = "[\n 1 true\n\n"; + JsonDocument doc = JsonDocument::fromJson(json, &error); + QVERIFY(doc.isEmpty()); + QCOMPARE(error.error, JsonParseError::MissingValueSeparator); + QCOMPARE(error.offset, 7); + } + { + JsonParseError error; + std::string json = "[\n nul"; + JsonDocument doc = JsonDocument::fromJson(json, &error); + QVERIFY(doc.isEmpty()); + QCOMPARE(error.error, JsonParseError::IllegalValue); + QCOMPARE(error.offset, 7); + } + { + JsonParseError error; + std::string json = "[\n nulzz"; + JsonDocument doc = JsonDocument::fromJson(json, &error); + QVERIFY(doc.isEmpty()); + QCOMPARE(error.error, JsonParseError::IllegalValue); + QCOMPARE(error.offset, 10); + } + { + JsonParseError error; + std::string json = "[\n tru"; + JsonDocument doc = JsonDocument::fromJson(json, &error); + QVERIFY(doc.isEmpty()); + QCOMPARE(error.error, JsonParseError::IllegalValue); + QCOMPARE(error.offset, 7); + } + { + JsonParseError error; + std::string json = "[\n trud]"; + JsonDocument doc = JsonDocument::fromJson(json, &error); + QVERIFY(doc.isEmpty()); + QCOMPARE(error.error, JsonParseError::IllegalValue); + QCOMPARE(error.offset, 10); + } + { + JsonParseError error; + std::string json = "[\n fal"; + JsonDocument doc = JsonDocument::fromJson(json, &error); + QVERIFY(doc.isEmpty()); + QCOMPARE(error.error, JsonParseError::IllegalValue); + QCOMPARE(error.offset, 7); + } + { + JsonParseError error; + std::string json = "[\n falsd]"; + JsonDocument doc = JsonDocument::fromJson(json, &error); + QVERIFY(doc.isEmpty()); + QCOMPARE(error.error, JsonParseError::IllegalValue); + QCOMPARE(error.offset, 11); + } + { + JsonParseError error; + std::string json = "[\n 11111"; + JsonDocument doc = JsonDocument::fromJson(json, &error); + QVERIFY(doc.isEmpty()); + QCOMPARE(error.error, JsonParseError::TerminationByNumber); + QCOMPARE(error.offset, 11); + } + { + JsonParseError error; + std::string json = "[\n -1E10000]"; + JsonDocument doc = JsonDocument::fromJson(json, &error); + QVERIFY(doc.isEmpty()); + QCOMPARE(error.error, JsonParseError::IllegalNumber); + QCOMPARE(error.offset, 14); + } + { + /* + JsonParseError error; + std::string json = "[\n -1e-10000]"; + JsonDocument doc = JsonDocument::fromJson(json, &error); + QVERIFY(doc.isEmpty()); + QCOMPARE(error.error, JsonParseError::IllegalNumber); + QCOMPARE(error.offset, 15); + */ + } + { + JsonParseError error; + std::string json = "[\n \"\\u12\"]"; + JsonDocument doc = JsonDocument::fromJson(json, &error); + QVERIFY(doc.isEmpty()); + QCOMPARE(error.error, JsonParseError::IllegalEscapeSequence); + QCOMPARE(error.offset, 11); + } + { + // This is not caught by the new parser as we don't parse + // UTF-8 anymore, but pass it as opaque blob. +// JsonParseError error; +// std::string json = "[\n \"foo" INVALID_UNICODE "bar\"]"; +// JsonDocument doc = JsonDocument::fromJson(json, &error); +// QVERIFY(doc.isEmpty()); +// QCOMPARE(error.error, JsonParseError::IllegalUTF8String); +// QCOMPARE(error.offset, 12); + } + { + JsonParseError error; + std::string json = "[\n \""; + JsonDocument doc = JsonDocument::fromJson(json, &error); + QVERIFY(doc.isEmpty()); + QCOMPARE(error.error, JsonParseError::UnterminatedString); + QCOMPARE(error.offset, 8); + } + { + JsonParseError error; + std::string json = "[\n \"c" UNICODE_DJE "a\\u12\"]"; + JsonDocument doc = JsonDocument::fromJson(json, &error); + QVERIFY(doc.isEmpty()); + QCOMPARE(error.error, JsonParseError::IllegalEscapeSequence); + QCOMPARE(error.offset, 15); + } + { + // This is not caught by the new parser as we don't parse + // UTF-8 anymore, but pass it as opaque blob. +// JsonParseError error; +// std::string json = "[\n \"c" UNICODE_DJE "a" INVALID_UNICODE "bar\"]"; +// JsonDocument doc = JsonDocument::fromJson(json, &error); +// QVERIFY(doc.isEmpty()); +// QCOMPARE(error.error, JsonParseError::IllegalUTF8String); +// QCOMPARE(error.offset, 13); + } + { + JsonParseError error; + std::string json = "[\n \"c" UNICODE_DJE "a ]"; + JsonDocument doc = JsonDocument::fromJson(json, &error); + QVERIFY(doc.isEmpty()); + QCOMPARE(error.error, JsonParseError::UnterminatedString); + QCOMPARE(error.offset, 14); + } +} + +void tst_Json::fromBinary() +{ + QFile file(testDataDir + QLatin1String("/test.json")); + file.open(QFile::ReadOnly); + std::string testJson = file.readAll().data(); + + JsonDocument doc = JsonDocument::fromJson(testJson); + JsonDocument outdoc = JsonDocument::fromBinaryData(doc.toBinaryData()); + QVERIFY(!outdoc.isNull()); + QCOMPARE(doc, outdoc); + +// // Can be used to re-create test.bjson: +// QFile b1file(testDataDir + QLatin1String("/test.bjson.x")); +// b1file.open(QFile::WriteOnly); +// std::string d = doc.toBinaryData(); +// b1file.write(d.data(), d.size()); +// b1file.close(); + + QFile bfile(testDataDir + QLatin1String("/test.bjson")); + bfile.open(QFile::ReadOnly); + std::string binary = bfile.readAll().toStdString(); + + JsonDocument bdoc = JsonDocument::fromBinaryData(binary); + QVERIFY(!bdoc.isNull()); + QCOMPARE(doc, bdoc); +} + +void tst_Json::toAndFromBinary_data() +{ + QTest::addColumn("filename"); + QTest::newRow("test.json") << (testDataDir + QLatin1String("/test.json")); + QTest::newRow("test2.json") << (testDataDir + QLatin1String("/test2.json")); +} + +void tst_Json::toAndFromBinary() +{ + QFETCH(QString, filename); + QFile file(filename); + QVERIFY(file.open(QFile::ReadOnly)); + std::string data = file.readAll().data(); + + JsonDocument doc = JsonDocument::fromJson(data); + QVERIFY(!doc.isNull()); + JsonDocument outdoc = JsonDocument::fromBinaryData(doc.toBinaryData()); + QVERIFY(!outdoc.isNull()); + QCOMPARE(doc, outdoc); +} + +void tst_Json::parseNumbers() +{ + { + // test number parsing + struct Numbers { + const char *str; + int n; + }; + Numbers numbers [] = { + { "0", 0 }, + { "1", 1 }, + { "10", 10 }, + { "-1", -1 }, + { "100000", 100000 }, + { "-999", -999 } + }; + int size = sizeof(numbers)/sizeof(Numbers); + for (int i = 0; i < size; ++i) { + std::string json = "[ "; + json += numbers[i].str; + json += " ]"; + JsonDocument doc = JsonDocument::fromJson(json); + QVERIFY(!doc.isEmpty()); + QCOMPARE(doc.isArray(), true); + QCOMPARE(doc.isObject(), false); + JsonArray array = doc.array(); + QCOMPARE(array.size(), 1); + JsonValue val = array.at(0); + QCOMPARE(val.type(), JsonValue::Double); + QCOMPARE(val.toDouble(), (double)numbers[i].n); + } + } + { + // test number parsing + struct Numbers { + const char *str; + double n; + }; + Numbers numbers [] = { + { "0", 0 }, + { "1", 1 }, + { "10", 10 }, + { "-1", -1 }, + { "100000", 100000 }, + { "-999", -999 }, + { "1.1", 1.1 }, + { "1e10", 1e10 }, + { "-1.1", -1.1 }, + { "-1e10", -1e10 }, + { "-1E10", -1e10 }, + { "1.1e10", 1.1e10 }, + { "1.1e308", 1.1e308 }, + { "-1.1e308", -1.1e308 }, + { "1.1e-308", 1.1e-308 }, + { "-1.1e-308", -1.1e-308 }, + { "1.1e+308", 1.1e+308 }, + { "-1.1e+308", -1.1e+308 }, + { "1.e+308", 1.e+308 }, + { "-1.e+308", -1.e+308 } + }; + int size = sizeof(numbers)/sizeof(Numbers); + for (int i = 0; i < size; ++i) { + std::string json = "[ "; + json += numbers[i].str; + json += " ]"; + JsonDocument doc = JsonDocument::fromJson(json); +#ifdef Q_OS_QNX + if (0 == QString::compare(numbers[i].str, "1.1e-308")) + QEXPECT_FAIL("", "See QTBUG-37066", Abort); +#endif + QVERIFY(!doc.isEmpty()); + QCOMPARE(doc.isArray(), true); + QCOMPARE(doc.isObject(), false); + JsonArray array = doc.array(); + QCOMPARE(array.size(), 1); + JsonValue val = array.at(0); + QCOMPARE(val.type(), JsonValue::Double); + QCOMPARE(val.toDouble(), numbers[i].n); + } + } +} + +void tst_Json::parseStrings() +{ + const char *strings [] = + { + "Foo", + "abc\\\"abc", + "abc\\\\abc", + "abc\\babc", + "abc\\fabc", + "abc\\nabc", + "abc\\rabc", + "abc\\tabc", + "abc\\u0019abc", + "abc" UNICODE_DJE "abc", + UNICODE_NON_CHARACTER + }; + int size = sizeof(strings)/sizeof(const char *); + + for (int i = 0; i < size; ++i) { + std::string json = "[\n \""; + json += strings[i]; + json += "\"\n]\n"; + JsonDocument doc = JsonDocument::fromJson(json); + QVERIFY(!doc.isEmpty()); + QCOMPARE(doc.isArray(), true); + QCOMPARE(doc.isObject(), false); + JsonArray array = doc.array(); + QCOMPARE(array.size(), 1); + JsonValue val = array.at(0); + QCOMPARE(val.type(), JsonValue::String); + + QCOMPARE(doc.toJson(), json); + } + + struct Pairs { + const char *in; + const char *out; + }; + Pairs pairs [] = { + { "abc\\/abc", "abc/abc" }, + { "abc\\u0402abc", "abc" UNICODE_DJE "abc" }, + { "abc\\u0065abc", "abceabc" }, + { "abc\\uFFFFabc", "abc" UNICODE_NON_CHARACTER "abc" } + }; + size = sizeof(pairs)/sizeof(Pairs); + + for (int i = 0; i < size; ++i) { + std::string json = "[\n \""; + json += pairs[i].in; + json += "\"\n]\n"; + std::string out = "[\n \""; + out += pairs[i].out; + out += "\"\n]\n"; + JsonDocument doc = JsonDocument::fromJson(json); + QVERIFY(!doc.isEmpty()); + QCOMPARE(doc.isArray(), true); + QCOMPARE(doc.isObject(), false); + JsonArray array = doc.array(); + QCOMPARE(array.size(), 1); + JsonValue val = array.at(0); + QCOMPARE(val.type(), JsonValue::String); + + QCOMPARE(doc.toJson(), out); + } + +} + +void tst_Json::parseDuplicateKeys() +{ + const char *json = "{ \"B\": true, \"A\": null, \"B\": false }"; + + JsonDocument doc = JsonDocument::fromJson(json); + QCOMPARE(doc.isObject(), true); + + JsonObject o = doc.object(); + QCOMPARE(o.size(), 2); + JsonObject::const_iterator it = o.constBegin(); + QCOMPARE(it.key(), std::string("A")); + QCOMPARE(it.value(), JsonValue()); + ++it; + QCOMPARE(it.key(), std::string("B")); + QCOMPARE(it.value(), JsonValue(false)); +} + +void tst_Json::testParser() +{ + QFile file(testDataDir + QLatin1String("/test.json")); + file.open(QFile::ReadOnly); + std::string testJson = file.readAll().data(); + + JsonDocument doc = JsonDocument::fromJson(testJson); + QVERIFY(!doc.isEmpty()); +} + +void tst_Json::compactArray() +{ + JsonArray array; + array.append("First Entry"); + array.append("Second Entry"); + array.append("Third Entry"); + JsonDocument doc(array); + auto s = doc.toBinaryData().size(); + array.removeAt(1); + doc.setArray(array); + QVERIFY(s > doc.toBinaryData().size()); + s = doc.toBinaryData().size(); + QCOMPARE(doc.toJson(), + std::string("[\n" + " \"First Entry\",\n" + " \"Third Entry\"\n" + "]\n")); + + array.removeAt(0); + doc.setArray(array); + QVERIFY(s > doc.toBinaryData().size()); + s = doc.toBinaryData().size(); + QCOMPARE(doc.toJson(), + std::string("[\n" + " \"Third Entry\"\n" + "]\n")); + + array.removeAt(0); + doc.setArray(array); + QVERIFY(s > doc.toBinaryData().size()); + s = doc.toBinaryData().size(); + QCOMPARE(doc.toJson(), + std::string("[\n" + "]\n")); + +} + +void tst_Json::compactObject() +{ + JsonObject object; + object.insert("Key1", "First Entry"); + object.insert("Key2", "Second Entry"); + object.insert("Key3", "Third Entry"); + JsonDocument doc(object); + auto s = doc.toBinaryData().size(); + object.remove("Key2"); + doc.setObject(object); + QVERIFY(s > doc.toBinaryData().size()); + s = doc.toBinaryData().size(); + QCOMPARE(doc.toJson(), + std::string("{\n" + " \"Key1\": \"First Entry\",\n" + " \"Key3\": \"Third Entry\"\n" + "}\n")); + + object.remove("Key1"); + doc.setObject(object); + QVERIFY(s > doc.toBinaryData().size()); + s = doc.toBinaryData().size(); + QCOMPARE(doc.toJson(), + std::string("{\n" + " \"Key3\": \"Third Entry\"\n" + "}\n")); + + object.remove("Key3"); + doc.setObject(object); + QVERIFY(s > doc.toBinaryData().size()); + s = doc.toBinaryData().size(); + QCOMPARE(doc.toJson(), + std::string("{\n" + "}\n")); + +} + +void tst_Json::validation() +{ + // this basically tests that we don't crash on corrupt data + QFile file(testDataDir + QLatin1String("/test.json")); + QVERIFY(file.open(QFile::ReadOnly)); + std::string testJson = file.readAll().data(); + QVERIFY(!testJson.empty()); + + JsonDocument doc = JsonDocument::fromJson(testJson); + QVERIFY(!doc.isNull()); + + std::string binary = doc.toBinaryData(); + + // only test the first 1000 bytes. Testing the full file takes too long + for (int i = 0; i < 1000; ++i) { + std::string corrupted = binary; + corrupted[i] = char(0xff); + JsonDocument doc = JsonDocument::fromBinaryData(corrupted); + if (doc.isNull()) + continue; + std::string json = doc.toJson(); + } + + + QFile file2(testDataDir + QLatin1String("/test3.json")); + file2.open(QFile::ReadOnly); + testJson = file2.readAll().data(); + QVERIFY(!testJson.empty()); + + doc = JsonDocument::fromJson(testJson); + QVERIFY(!doc.isNull()); + + binary = doc.toBinaryData(); + + for (size_t i = 0; i < binary.size(); ++i) { + std::string corrupted = binary; + corrupted[i] = char(0xff); + JsonDocument doc = JsonDocument::fromBinaryData(corrupted); + if (doc.isNull()) + continue; + std::string json = doc.toJson(); + + corrupted = binary; + corrupted[i] = 0x00; + doc = JsonDocument::fromBinaryData(corrupted); + if (doc.isNull()) + continue; + json = doc.toJson(); + } +} + +void tst_Json::assignToDocument() +{ + { + const char *json = "{ \"inner\": { \"key\": true } }"; + JsonDocument doc = JsonDocument::fromJson(json); + + JsonObject o = doc.object(); + JsonValue inner = o.value("inner"); + + JsonDocument innerDoc(inner.toObject()); + + QVERIFY(innerDoc != doc); + QCOMPARE(innerDoc.object(), inner.toObject()); + } + { + const char *json = "[ [ true ] ]"; + JsonDocument doc = JsonDocument::fromJson(json); + + JsonArray a = doc.array(); + JsonValue inner = a.at(0); + + JsonDocument innerDoc(inner.toArray()); + + QVERIFY(innerDoc != doc); + QCOMPARE(innerDoc.array(), inner.toArray()); + } +} + + +void tst_Json::testDuplicateKeys() +{ + JsonObject obj; + obj.insert("foo", "bar"); + obj.insert("foo", "zap"); + QCOMPARE(obj.size(), 1); + QCOMPARE(obj.value("foo").toString(), std::string("zap")); +} + +void tst_Json::testCompaction() +{ + // modify object enough times to trigger compactionCounter + // and make sure the data is still valid + JsonObject obj; + for (int i = 0; i < 33; ++i) { + obj.remove("foo"); + obj.insert("foo", "bar"); + } + QCOMPARE(obj.size(), 1); + QCOMPARE(obj.value("foo").toString(), std::string("bar")); + + JsonDocument doc = JsonDocument::fromBinaryData(JsonDocument(obj).toBinaryData()); + QVERIFY(!doc.isNull()); + QVERIFY(!doc.isEmpty()); + QCOMPARE(doc.isArray(), false); + QCOMPARE(doc.isObject(), true); + QCOMPARE(doc.object(), obj); +} + +void tst_Json::testCompactionError() +{ + JsonObject schemaObject; + schemaObject.insert("_Type", "_SchemaType"); + schemaObject.insert("name", "Address"); + schemaObject.insert("schema", JsonObject()); + { + JsonObject content(schemaObject); + JsonDocument doc(content); + QVERIFY(!doc.isNull()); + QByteArray hash = QCryptographicHash::hash(doc.toBinaryData().data(), QCryptographicHash::Md5).toHex(); + schemaObject.insert("_Version", hash.data()); + } + + JsonObject schema; + schema.insert("streetNumber", schema.value("number").toObject()); + schemaObject.insert("schema", schema); + { + JsonObject content(schemaObject); + content.remove("_Uuid"); + content.remove("_Version"); + JsonDocument doc(content); + QVERIFY(!doc.isNull()); + QByteArray hash = QCryptographicHash::hash(doc.toBinaryData().data(), QCryptographicHash::Md5).toHex(); + schemaObject.insert("_Version", hash.data()); + } +} + +void tst_Json::parseUnicodeEscapes() +{ + const std::string json = "[ \"A\\u00e4\\u00C4\" ]"; + + JsonDocument doc = JsonDocument::fromJson(json); + JsonArray array = doc.array(); + + QString result = QLatin1String("A"); + result += QChar(0xe4); + result += QChar(0xc4); + + std::string expected = result.toUtf8().data(); + + QCOMPARE(array.first().toString(), expected); +} + +void tst_Json::assignObjects() +{ + const char *json = + "[ { \"Key\": 1 }, { \"Key\": 2 } ]"; + + JsonDocument doc = JsonDocument::fromJson(json); + JsonArray array = doc.array(); + + JsonObject object = array.at(0).toObject(); + QCOMPARE(object.value("Key").toDouble(), 1.); + + object = array.at(1).toObject(); + QCOMPARE(object.value("Key").toDouble(), 2.); +} + +void tst_Json::assignArrays() +{ + const char *json = + "[ [ 1 ], [ 2 ] ]"; + + JsonDocument doc = JsonDocument::fromJson(json); + JsonArray array = doc.array(); + + JsonArray inner = array.at(0).toArray() ; + QCOMPARE(inner.at(0).toDouble(), 1.); + + inner= array.at(1).toArray(); + QCOMPARE(inner.at(0).toDouble(), 2.); +} + +void tst_Json::testTrailingComma() +{ + const char *jsons[] = { "{ \"Key\": 1, }", "[ { \"Key\": 1 }, ]" }; + + for (unsigned i = 0; i < sizeof(jsons)/sizeof(jsons[0]); ++i) { + JsonParseError error; + JsonDocument doc = JsonDocument::fromJson(jsons[i], &error); + QCOMPARE(error.error, JsonParseError::MissingObject); + } +} + +void tst_Json::testDetachBug() +{ + JsonObject dynamic; + JsonObject embedded; + + JsonObject local; + + embedded.insert("Key1", "Value1"); + embedded.insert("Key2", "Value2"); + dynamic.insert("Bogus", "bogusValue"); + dynamic.insert("embedded", embedded); + local = dynamic.value("embedded").toObject(); + + dynamic.remove("embedded"); + + QCOMPARE(local.keys().size(), size_t(2)); + local.remove("Key1"); + local.remove("Key2"); + QCOMPARE(local.keys().size(), size_t(0)); + + local.insert("Key1", "anotherValue"); + QCOMPARE(local.keys().size(), size_t(1)); +} + +void tst_Json::valueEquals() +{ + QCOMPARE(JsonValue(), JsonValue()); + QVERIFY(JsonValue() != JsonValue(JsonValue::Undefined)); + QVERIFY(JsonValue() != JsonValue(true)); + QVERIFY(JsonValue() != JsonValue(1.)); + QVERIFY(JsonValue() != JsonValue(JsonArray())); + QVERIFY(JsonValue() != JsonValue(JsonObject())); + + QCOMPARE(JsonValue(true), JsonValue(true)); + QVERIFY(JsonValue(true) != JsonValue(false)); + QVERIFY(JsonValue(true) != JsonValue(JsonValue::Undefined)); + QVERIFY(JsonValue(true) != JsonValue()); + QVERIFY(JsonValue(true) != JsonValue(1.)); + QVERIFY(JsonValue(true) != JsonValue(JsonArray())); + QVERIFY(JsonValue(true) != JsonValue(JsonObject())); + + QCOMPARE(JsonValue(1), JsonValue(1)); + QVERIFY(JsonValue(1) != JsonValue(2)); + QCOMPARE(JsonValue(1), JsonValue(1.)); + QVERIFY(JsonValue(1) != JsonValue(1.1)); + QVERIFY(JsonValue(1) != JsonValue(JsonValue::Undefined)); + QVERIFY(JsonValue(1) != JsonValue()); + QVERIFY(JsonValue(1) != JsonValue(true)); + QVERIFY(JsonValue(1) != JsonValue(JsonArray())); + QVERIFY(JsonValue(1) != JsonValue(JsonObject())); + + QCOMPARE(JsonValue(1.), JsonValue(1.)); + QVERIFY(JsonValue(1.) != JsonValue(2.)); + QVERIFY(JsonValue(1.) != JsonValue(JsonValue::Undefined)); + QVERIFY(JsonValue(1.) != JsonValue()); + QVERIFY(JsonValue(1.) != JsonValue(true)); + QVERIFY(JsonValue(1.) != JsonValue(JsonArray())); + QVERIFY(JsonValue(1.) != JsonValue(JsonObject())); + + QCOMPARE(JsonValue(JsonArray()), JsonValue(JsonArray())); + JsonArray nonEmptyArray; + nonEmptyArray.append(true); + QVERIFY(JsonValue(JsonArray()) != nonEmptyArray); + QVERIFY(JsonValue(JsonArray()) != JsonValue(JsonValue::Undefined)); + QVERIFY(JsonValue(JsonArray()) != JsonValue()); + QVERIFY(JsonValue(JsonArray()) != JsonValue(true)); + QVERIFY(JsonValue(JsonArray()) != JsonValue(1.)); + QVERIFY(JsonValue(JsonArray()) != JsonValue(JsonObject())); + + QCOMPARE(JsonValue(JsonObject()), JsonValue(JsonObject())); + JsonObject nonEmptyObject; + nonEmptyObject.insert("Key", true); + QVERIFY(JsonValue(JsonObject()) != nonEmptyObject); + QVERIFY(JsonValue(JsonObject()) != JsonValue(JsonValue::Undefined)); + QVERIFY(JsonValue(JsonObject()) != JsonValue()); + QVERIFY(JsonValue(JsonObject()) != JsonValue(true)); + QVERIFY(JsonValue(JsonObject()) != JsonValue(1.)); + QVERIFY(JsonValue(JsonObject()) != JsonValue(JsonArray())); + + QCOMPARE(JsonValue("foo"), JsonValue("foo")); + QCOMPARE(JsonValue("\x66\x6f\x6f"), JsonValue("foo")); + QCOMPARE(JsonValue("\x62\x61\x72"), JsonValue("bar")); + QCOMPARE(JsonValue(UNICODE_NON_CHARACTER), JsonValue(UNICODE_NON_CHARACTER)); + QCOMPARE(JsonValue(UNICODE_DJE), JsonValue(UNICODE_DJE)); + QCOMPARE(JsonValue("\xc3\xa9"), JsonValue("\xc3\xa9")); +} + +void tst_Json::objectEquals_data() +{ + QTest::addColumn("left"); + QTest::addColumn("right"); + QTest::addColumn("result"); + + QTest::newRow("two defaults") << JsonObject() << JsonObject() << true; + + JsonObject object1; + object1.insert("property", 1); + JsonObject object2; + object2["property"] = 1; + JsonObject object3; + object3.insert("property1", 1); + object3.insert("property2", 2); + + QTest::newRow("the same object (1 vs 2)") << object1 << object2 << true; + QTest::newRow("the same object (3 vs 3)") << object3 << object3 << true; + QTest::newRow("different objects (2 vs 3)") << object2 << object3 << false; + QTest::newRow("object vs default") << object1 << JsonObject() << false; + + JsonObject empty; + empty.insert("property", 1); + empty.take("property"); + QTest::newRow("default vs empty") << JsonObject() << empty << true; + QTest::newRow("empty vs empty") << empty << empty << true; + QTest::newRow("object vs empty") << object1 << empty << false; + + JsonObject referencedEmpty; + referencedEmpty["undefined"]; + QTest::newRow("referenced empty vs referenced empty") << referencedEmpty << referencedEmpty << true; + QTest::newRow("referenced empty vs object") << referencedEmpty << object1 << false; + + JsonObject referencedObject1; + referencedObject1.insert("property", 1); + referencedObject1["undefined"]; + JsonObject referencedObject2; + referencedObject2.insert("property", 1); + referencedObject2["aaaaaaaaa"]; // earlier then "property" + referencedObject2["zzzzzzzzz"]; // after "property" + QTest::newRow("referenced object vs default") << referencedObject1 << JsonObject() << false; + QTest::newRow("referenced object vs referenced object") << referencedObject1 << referencedObject1 << true; + QTest::newRow("referenced object vs object (different)") << referencedObject1 << object3 << false; +} + +void tst_Json::objectEquals() +{ + QFETCH(JsonObject, left); + QFETCH(JsonObject, right); + QFETCH(bool, result); + + QCOMPARE(left == right, result); + QCOMPARE(right == left, result); + + // invariants checks + QCOMPARE(left, left); + QCOMPARE(right, right); + QCOMPARE(left != right, !result); + QCOMPARE(right != left, !result); + + // The same but from JsonValue perspective + QCOMPARE(JsonValue(left) == JsonValue(right), result); + QCOMPARE(JsonValue(left) != JsonValue(right), !result); + QCOMPARE(JsonValue(right) == JsonValue(left), result); + QCOMPARE(JsonValue(right) != JsonValue(left), !result); +} + +void tst_Json::arrayEquals_data() +{ + QTest::addColumn("left"); + QTest::addColumn("right"); + QTest::addColumn("result"); + + QTest::newRow("two defaults") << JsonArray() << JsonArray() << true; + + JsonArray array1; + array1.append(1); + JsonArray array2; + array2.append(2111); + array2[0] = 1; + JsonArray array3; + array3.insert(0, 1); + array3.insert(1, 2); + + QTest::newRow("the same array (1 vs 2)") << array1 << array2 << true; + QTest::newRow("the same array (3 vs 3)") << array3 << array3 << true; + QTest::newRow("different arrays (2 vs 3)") << array2 << array3 << false; + QTest::newRow("array vs default") << array1 << JsonArray() << false; + + JsonArray empty; + empty.append(1); + empty.takeAt(0); + QTest::newRow("default vs empty") << JsonArray() << empty << true; + QTest::newRow("empty vs default") << empty << JsonArray() << true; + QTest::newRow("empty vs empty") << empty << empty << true; + QTest::newRow("array vs empty") << array1 << empty << false; +} + +void tst_Json::arrayEquals() +{ + QFETCH(JsonArray, left); + QFETCH(JsonArray, right); + QFETCH(bool, result); + + QCOMPARE(left == right, result); + QCOMPARE(right == left, result); + + // invariants checks + QCOMPARE(left, left); + QCOMPARE(right, right); + QCOMPARE(left != right, !result); + QCOMPARE(right != left, !result); + + // The same but from JsonValue perspective + QCOMPARE(JsonValue(left) == JsonValue(right), result); + QCOMPARE(JsonValue(left) != JsonValue(right), !result); + QCOMPARE(JsonValue(right) == JsonValue(left), result); + QCOMPARE(JsonValue(right) != JsonValue(left), !result); +} + +void tst_Json::bom() +{ + QFile file(testDataDir + QLatin1String("/bom.json")); + file.open(QFile::ReadOnly); + std::string json = file.readAll().data(); + + // Import json document into a JsonDocument + JsonParseError error; + JsonDocument doc = JsonDocument::fromJson(json, &error); + + QVERIFY(!doc.isNull()); + QCOMPARE(error.error, JsonParseError::NoError); +} + +void tst_Json::nesting() +{ + // check that we abort parsing too deeply nested json documents. + // this is to make sure we don't crash because the parser exhausts the + // stack. + + const char *array_data = + "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" + "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" + "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" + "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" + "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" + "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" + "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" + "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" + "]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]" + "]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]" + "]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]" + "]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]" + "]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]" + "]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]" + "]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]" + "]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]"; + + std::string json(array_data); + JsonParseError error; + JsonDocument doc = JsonDocument::fromJson(json, &error); + + QVERIFY(!doc.isNull()); + QCOMPARE(error.error, JsonParseError::NoError); + + json = '[' + json + ']'; + doc = JsonDocument::fromJson(json, &error); + + QVERIFY(doc.isNull()); + QCOMPARE(error.error, JsonParseError::DeepNesting); + + json = std::string("true "); + + for (int i = 0; i < 1024; ++i) + json = "{ \"Key\": " + json + " }"; + + doc = JsonDocument::fromJson(json, &error); + + QVERIFY(!doc.isNull()); + QCOMPARE(error.error, JsonParseError::NoError); + + json = '[' + json + ']'; + doc = JsonDocument::fromJson(json, &error); + + QVERIFY(doc.isNull()); + QCOMPARE(error.error, JsonParseError::DeepNesting); + +} + +void tst_Json::longStrings() +{ +#if 0 + // test around 15 and 16 bit boundaries, as these are limits + // in the data structures (for Latin1String in qjson_p.h) + QString s(0x7ff0, 'a'); + for (int i = 0x7ff0; i < 0x8010; i++) { + s.append(QLatin1Char('c')); + + QMap map; + map["key"] = s; + + /* Create a JsonDocument from the QMap ... */ + JsonDocument d1 = JsonDocument::fromVariant(QVariant(map)); + /* ... and a std::string from the JsonDocument */ + std::string a1 = d1.toJson(); + + /* Create a JsonDocument from the std::string ... */ + JsonDocument d2 = JsonDocument::fromJson(a1); + /* ... and a std::string from the JsonDocument */ + std::string a2 = d2.toJson(); + QCOMPARE(a1, a2); + } + + s = QString(0xfff0, 'a'); + for (int i = 0xfff0; i < 0x10010; i++) { + s.append(QLatin1Char('c')); + + QMap map; + map["key"] = s; + + /* Create a JsonDocument from the QMap ... */ + JsonDocument d1 = JsonDocument::fromVariant(QVariant(map)); + /* ... and a std::string from the JsonDocument */ + std::string a1 = d1.toJson(); + + /* Create a JsonDocument from the std::string ... */ + JsonDocument d2 = JsonDocument::fromJson(a1); + /* ... and a std::string from the JsonDocument */ + std::string a2 = d2.toJson(); + QCOMPARE(a1, a2); + } +#endif +} + +void tst_Json::testJsonValueRefDefault() +{ + JsonObject empty; + + QCOMPARE(empty["n/a"].toString(), std::string()); + QCOMPARE(empty["n/a"].toString("default"), std::string("default")); + + QCOMPARE(empty["n/a"].toBool(), false); + QCOMPARE(empty["n/a"].toBool(true), true); + + QCOMPARE(empty["n/a"].toInt(), 0); + QCOMPARE(empty["n/a"].toInt(42), 42); + + QCOMPARE(empty["n/a"].toDouble(), 0.0); + QCOMPARE(empty["n/a"].toDouble(42.0), 42.0); +} + +void tst_Json::arrayInitializerList() +{ +#ifndef Q_COMPILER_INITIALIZER_LISTS + QSKIP("initializer_list is enabled only with c++11 support"); +#else + QVERIFY(JsonArray{}.isEmpty()); + QCOMPARE(JsonArray{"one"}.count(), 1); + QCOMPARE(JsonArray{1}.count(), 1); + + { + JsonArray a{1.3, "hello", 0}; + QCOMPARE(JsonValue(a[0]), JsonValue(1.3)); + QCOMPARE(JsonValue(a[1]), JsonValue("hello")); + QCOMPARE(JsonValue(a[2]), JsonValue(0)); + QCOMPARE(a.count(), 3); + } + { + JsonObject o; + o["property"] = 1; + JsonArray a1 {o}; + QCOMPARE(a1.count(), 1); + QCOMPARE(a1[0].toObject(), o); + + JsonArray a2 {o, 23}; + QCOMPARE(a2.count(), 2); + QCOMPARE(a2[0].toObject(), o); + QCOMPARE(JsonValue(a2[1]), JsonValue(23)); + + JsonArray a3 { a1, o, a2 }; + QCOMPARE(JsonValue(a3[0]), JsonValue(a1)); + QCOMPARE(JsonValue(a3[1]), JsonValue(o)); + QCOMPARE(JsonValue(a3[2]), JsonValue(a2)); + + JsonArray a4 { 1, JsonArray{1,2,3}, JsonArray{"hello", 2}, JsonObject{{"one", 1}} }; + QCOMPARE(a4.count(), 4); + QCOMPARE(JsonValue(a4[0]), JsonValue(1)); + + { + JsonArray a41 = a4[1].toArray(); + JsonArray a42 = a4[2].toArray(); + JsonObject a43 = a4[3].toObject(); + QCOMPARE(a41.count(), 3); + QCOMPARE(a42.count(), 2); + QCOMPARE(a43.count(), 1); + + QCOMPARE(JsonValue(a41[2]), JsonValue(3)); + QCOMPARE(JsonValue(a42[1]), JsonValue(2)); + QCOMPARE(JsonValue(a43["one"]), JsonValue(1)); + } + } +#endif +} + +void tst_Json::objectInitializerList() +{ +#ifndef Q_COMPILER_INITIALIZER_LISTS + QSKIP("initializer_list is enabled only with c++11 support"); +#else + QVERIFY(JsonObject{}.isEmpty()); + + { // one property + JsonObject one {{"one", 1}}; + QCOMPARE(one.count(), 1); + QVERIFY(one.contains("one")); + QCOMPARE(JsonValue(one["one"]), JsonValue(1)); + } + { // two properties + JsonObject two { + {"one", 1}, + {"two", 2} + }; + QCOMPARE(two.count(), 2); + QVERIFY(two.contains("one")); + QVERIFY(two.contains("two")); + QCOMPARE(JsonValue(two["one"]), JsonValue(1)); + QCOMPARE(JsonValue(two["two"]), JsonValue(2)); + } + { // nested object + JsonObject object{{"nested", JsonObject{{"innerProperty", 2}}}}; + QCOMPARE(object.count(), 1); + QVERIFY(object.contains("nested")); + QVERIFY(object["nested"].isObject()); + + JsonObject nested = object["nested"].toObject(); + QCOMPARE(JsonValue(nested["innerProperty"]), JsonValue(2)); + } + { // nested array + JsonObject object{{"nested", JsonArray{"innerValue", 2.1, "bum cyk cyk"}}}; + QCOMPARE(object.count(), 1); + QVERIFY(object.contains("nested")); + QVERIFY(object["nested"].isArray()); + + JsonArray nested = object["nested"].toArray(); + QCOMPARE(nested.count(), 3); + QCOMPARE(JsonValue(nested[0]), JsonValue("innerValue")); + QCOMPARE(JsonValue(nested[1]), JsonValue(2.1)); + } +#endif +} + +void tst_Json::unicodeKeys() +{ + std::string json = "{" + "\"x\\u2090_1\": \"hello_1\"," + "\"y\\u2090_2\": \"hello_2\"," + "\"T\\u2090_3\": \"hello_3\"," + "\"xyz_4\": \"hello_4\"," + "\"abc_5\": \"hello_5\"" + "}"; + + JsonParseError error; + JsonDocument doc = JsonDocument::fromJson(json, &error); + QCOMPARE(error.error, JsonParseError::NoError); + JsonObject o = doc.object(); + + QCOMPARE(o.keys().size(), size_t(5)); + Q_FOREACH (const std::string &k, o.keys()) { + QByteArray key(k.data()); + std::string suffix = key.mid(key.indexOf('_')).data(); + QCOMPARE(o[key.data()].toString(), "hello" + suffix); + } +} + +void tst_Json::garbageAtEnd() +{ + JsonParseError error; + JsonDocument doc = JsonDocument::fromJson("{},", &error); + QCOMPARE(error.error, JsonParseError::GarbageAtEnd); + QCOMPARE(error.offset, 2); + QVERIFY(doc.isEmpty()); + + doc = JsonDocument::fromJson("{} ", &error); + QCOMPARE(error.error, JsonParseError::NoError); + QVERIFY(!doc.isEmpty()); +} + +void tst_Json::removeNonLatinKey() +{ + const std::string nonLatinKeyName = "Атрибут100500"; + + JsonObject sourceObject; + + sourceObject.insert("code", 1); + sourceObject.remove("code"); + + sourceObject.insert(nonLatinKeyName, 1); + + const std::string json = JsonDocument(sourceObject).toJson(); + const JsonObject restoredObject = JsonDocument::fromJson(json).object(); + + QCOMPARE(sourceObject.keys(), restoredObject.keys()); + QVERIFY(sourceObject.contains(nonLatinKeyName)); + QVERIFY(restoredObject.contains(nonLatinKeyName)); +} + +QTEST_MAIN(tst_Json) + +#include "tst_json.moc" diff --git a/tests/auto/qml/qmlprojectmanager/fileformat/fileformat.pro b/tests/auto/qml/qmlprojectmanager/fileformat/fileformat.pro index 567364c106d..a85d683f92f 100644 --- a/tests/auto/qml/qmlprojectmanager/fileformat/fileformat.pro +++ b/tests/auto/qml/qmlprojectmanager/fileformat/fileformat.pro @@ -1,7 +1,7 @@ QTC_LIB_DEPENDS += qmljs include(../../../qttest.pri) -QT += script +QT += qml PLUGIN_DIR=$$IDE_SOURCE_TREE/src/plugins/qmlprojectmanager diff --git a/tests/auto/qml/reformatter/qmlsyntax.qml b/tests/auto/qml/reformatter/qmlsyntax.qml index 9348bd4f2b3..ded863106a7 100644 --- a/tests/auto/qml/reformatter/qmlsyntax.qml +++ b/tests/auto/qml/reformatter/qmlsyntax.qml @@ -10,6 +10,9 @@ Text { property alias bar: x property list pro default property int def + property var baz: Rectangle { + width: 20 + } // script binding x: x + y diff --git a/tests/benchmarks/json/json.pro b/tests/benchmarks/json/json.pro new file mode 100644 index 00000000000..93569682659 --- /dev/null +++ b/tests/benchmarks/json/json.pro @@ -0,0 +1,14 @@ +TARGET = tst_bench_json +QT = core testlib +CONFIG -= app_bundle + +SOURCES += tst_bench_json.cpp + +TESTDATA = numbers.json test.json + + +INCLUDEPATH += ../../../src/shared/json + +#DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII + +include(../../../src/shared/json/json.pri) diff --git a/tests/benchmarks/json/numbers.json b/tests/benchmarks/json/numbers.json new file mode 100644 index 00000000000..469156a78a3 --- /dev/null +++ b/tests/benchmarks/json/numbers.json @@ -0,0 +1,19 @@ +[ + { + "integer": 1234567890, + "real": -9876.543210, + "e": 0.123456789e-12, + "E": 1.234567890E+34, + "": 23456789012E66, + "zero": 0, + "one": 1 + }, + [ + -1234567890, + -1234567890, + -1234567890, + 1234567890, + 1234567890, + 1234567890 + ] +] diff --git a/tests/benchmarks/json/test.json b/tests/benchmarks/json/test.json new file mode 100644 index 00000000000..330756894a6 --- /dev/null +++ b/tests/benchmarks/json/test.json @@ -0,0 +1,66 @@ +[ + "JSON Test Pattern pass1", + {"object with 1 member":["array with 1 element"]}, + {}, + [], + -42, + true, + false, + null, + { + "integer": 1234567890, + "real": -9876.543210, + "e": 0.123456789e-12, + "E": 1.234567890E+34, + "": 23456789012E66, + "zero": 0, + "one": 1, + "space": " ", + "quote": "\"", + "backslash": "\\", + "controls": "\b\f\n\r\t", + "slash": "/ & \/", + "alpha": "abcdefghijklmnopqrstuvwxyz", + "ALPHA": "ABCDEFGHIJKLMNOPQRSTUVWXYZ", + "digit": "0123456789", + "0123456789": "digit", + "special": "`1~!@#$%^&*()_+-={\':[,]}|;.?", + "hex": "\u0123\u4567\u89AB\uCDEF\uabcd\uef4A", + "true": true, + "false": false, + "null": null, + "array":[ ], + "object":{ }, + "address": "50 St. James Street", + "url": "nix", + "comment": "// /* */": " ", + " s p a c e d " :[1,2 , 3 + +, + +4 , 5 , 6 ,7 ],"compact":[1,2,3,4,5,6,7], + "jsontext": "{\"object with 1 member\":[\"array with 1 element\"]}", + "quotes": "" \u0022 %22 0x22 034 "", + "\/\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:\',./<>?" : "A key can be any string" + }, + 0.5 ,98.6 +, +99.44 +, + +1066, +1e1, +0.1e1, +1e-1, +1e00, +2e+00, +2e-00, +"rosebud", +{"foo": "bar"}, +{"classification":{"relevancyScore":1000,"searchUrl":{"value":"nix"}},"products":{"priceSet":{"minPrice":{"value":"$0.01","integral":1},"maxPrice":{"value":"$4,833.99","integral":483399}},"product":[{"type":"PRODUCT","title":"Silicone c","description":"Elite Hori","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":310711221747712.000000,"priceSet":{"minPrice":{"value":"$1.56","integral":156},"maxPrice":{"value":"$29.99","integral":2999},"stores":14},"id":1968262863,"categoryId":8515},{"type":"PRODUCT","title":"Nonslip Ch","description":"Specificat","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":175580930637824.000000,"priceSet":{"minPrice":{"value":"$0.45","integral":45},"maxPrice":{"value":"$194.95","integral":19495},"stores":34},"id":2534935499,"categoryId":8515},{"type":"PRODUCT","title":"Plastic Ca","description":"Descriptio","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":132488642953216.000000,"priceSet":{"minPrice":{"value":"$0.99","integral":99},"maxPrice":{"value":"$303.68","integral":30368},"stores":33},"id":2305624670,"categoryId":8515},{"type":"PRODUCT","title":"Protective","description":"Made of hi","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":108614681362432.000000,"priceSet":{"minPrice":{"value":"$1.70","integral":170},"maxPrice":{"value":"$99.99","integral":9999},"stores":11},"id":2120981405,"categoryId":8515},{"type":"PRODUCT","title":"P® 4","description":"Do more th","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":96203484168192.000000,"priceSet":{"minPrice":{"value":"$2.49","integral":249},"maxPrice":{"value":"$79.95","integral":7995},"stores":16},"id":2203798762,"categoryId":8515},{"type":"PRODUCT","title":"Case Refle","description":"NCAA iPhon","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":84727583211520.000000,"priceSet":{"minPrice":{"value":"$0.69","integral":69},"maxPrice":{"value":"$75.52","integral":7552},"stores":59},"id":1114627445,"categoryId":8515},{"type":"PRODUCT","title":"Infuse Pro","description":"Protect an","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":80831066406912.000000,"priceSet":{"minPrice":{"value":"$0.59","integral":59},"maxPrice":{"value":"$79.00","integral":7900},"stores":24},"id":2557462717,"categoryId":8515},{"type":"PRODUCT","title":"Dragonfly ","description":"d","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":70900229603328.000000,"priceSet":{"minPrice":{"value":"$1.05","integral":105},"maxPrice":{"value":"$94.49","integral":9449},"stores":30},"id":2442061740,"categoryId":8515},{"type":"PRODUCT","title":"Pho","description":"Snap on Ap","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":65194915004416.000000,"priceSet":{"minPrice":{"value":"$0.01","integral":1},"maxPrice":{"value":"$414.99","integral":41499},"stores":39},"id":2004746863,"categoryId":8515},{"type":"PRODUCT","title":"Otterbox i","description":"Your iPhon","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":61515478597632.000000,"priceSet":{"minPrice":{"value":"$3.28","integral":328},"maxPrice":{"value":"$110.65","integral":11065},"stores":25},"id":2584611575,"categoryId":8515}],"includedResults":10,"totalResults":2000}}, +{"classification":{"relevancyScore":1000,"searchUrl":{"value":"nix"}},"products":{"priceSet":{"minPrice":{"value":"$0.01","integral":1},"maxPrice":{"value":"$4,833.99","integral":483399}},"product":[{"type":"PRODUCT","title":"Silicone c","description":"Elite Hori","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":310711221747712.000000,"priceSet":{"minPrice":{"value":"$1.56","integral":156},"maxPrice":{"value":"$29.99","integral":2999},"stores":14},"id":1968262863,"categoryId":8515},{"type":"PRODUCT","title":"Nonslip Ch","description":"Specificat","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":175580930637824.000000,"priceSet":{"minPrice":{"value":"$0.45","integral":45},"maxPrice":{"value":"$194.95","integral":19495},"stores":34},"id":2534935499,"categoryId":8515},{"type":"PRODUCT","title":"Plastic Ca","description":"Descriptio","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":132488642953216.000000,"priceSet":{"minPrice":{"value":"$0.99","integral":99},"maxPrice":{"value":"$303.68","integral":30368},"stores":33},"id":2305624670,"categoryId":8515},{"type":"PRODUCT","title":"Protective","description":"Made of hi","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":108614681362432.000000,"priceSet":{"minPrice":{"value":"$1.70","integral":170},"maxPrice":{"value":"$99.99","integral":9999},"stores":11},"id":2120981405,"categoryId":8515},{"type":"PRODUCT","title":"P® 4","description":"Do more th","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":96203484168192.000000,"priceSet":{"minPrice":{"value":"$2.49","integral":249},"maxPrice":{"value":"$79.95","integral":7995},"stores":16},"id":2203798762,"categoryId":8515},{"type":"PRODUCT","title":"Case Refle","description":"NCAA iPhon","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":84727583211520.000000,"priceSet":{"minPrice":{"value":"$0.69","integral":69},"maxPrice":{"value":"$75.52","integral":7552},"stores":59},"id":1114627445,"categoryId":8515},{"type":"PRODUCT","title":"Infuse Pro","description":"Protect an","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":80831066406912.000000,"priceSet":{"minPrice":{"value":"$0.59","integral":59},"maxPrice":{"value":"$79.00","integral":7900},"stores":24},"id":2557462717,"categoryId":8515},{"type":"PRODUCT","title":"Dragonfly ","description":"d","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":70900229603328.000000,"priceSet":{"minPrice":{"value":"$1.05","integral":105},"maxPrice":{"value":"$94.49","integral":9449},"stores":30},"id":2442061740,"categoryId":8515},{"type":"PRODUCT","title":"Pho","description":"Snap on Ap","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":65194915004416.000000,"priceSet":{"minPrice":{"value":"$0.01","integral":1},"maxPrice":{"value":"$414.99","integral":41499},"stores":39},"id":2004746863,"categoryId":8515},{"type":"PRODUCT","title":"Otterbox i","description":"Your iPhon","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":61515478597632.000000,"priceSet":{"minPrice":{"value":"$3.28","integral":328},"maxPrice":{"value":"$110.65","integral":11065},"stores":25},"id":2584611575,"categoryId":8515}],"includedResults":10,"totalResults":2000}}, +{"classification":{"relevancyScore":1000,"searchUrl":{"value":"nix"}},"products":{"priceSet":{"minPrice":{"value":"$0.01","integral":1},"maxPrice":{"value":"$4,833.99","integral":483399}},"product":[{"type":"PRODUCT","title":"Silicone c","description":"Elite Hori","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":310711221747712.000000,"priceSet":{"minPrice":{"value":"$1.56","integral":156},"maxPrice":{"value":"$29.99","integral":2999},"stores":14},"id":1968262863,"categoryId":8515},{"type":"PRODUCT","title":"Nonslip Ch","description":"Specificat","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":175580930637824.000000,"priceSet":{"minPrice":{"value":"$0.45","integral":45},"maxPrice":{"value":"$194.95","integral":19495},"stores":34},"id":2534935499,"categoryId":8515},{"type":"PRODUCT","title":"Plastic Ca","description":"Descriptio","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":132488642953216.000000,"priceSet":{"minPrice":{"value":"$0.99","integral":99},"maxPrice":{"value":"$303.68","integral":30368},"stores":33},"id":2305624670,"categoryId":8515},{"type":"PRODUCT","title":"Protective","description":"Made of hi","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":108614681362432.000000,"priceSet":{"minPrice":{"value":"$1.70","integral":170},"maxPrice":{"value":"$99.99","integral":9999},"stores":11},"id":2120981405,"categoryId":8515},{"type":"PRODUCT","title":"P® 4","description":"Do more th","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":96203484168192.000000,"priceSet":{"minPrice":{"value":"$2.49","integral":249},"maxPrice":{"value":"$79.95","integral":7995},"stores":16},"id":2203798762,"categoryId":8515},{"type":"PRODUCT","title":"Case Refle","description":"NCAA iPhon","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":84727583211520.000000,"priceSet":{"minPrice":{"value":"$0.69","integral":69},"maxPrice":{"value":"$75.52","integral":7552},"stores":59},"id":1114627445,"categoryId":8515},{"type":"PRODUCT","title":"Infuse Pro","description":"Protect an","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":80831066406912.000000,"priceSet":{"minPrice":{"value":"$0.59","integral":59},"maxPrice":{"value":"$79.00","integral":7900},"stores":24},"id":2557462717,"categoryId":8515},{"type":"PRODUCT","title":"Dragonfly ","description":"d","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":70900229603328.000000,"priceSet":{"minPrice":{"value":"$1.05","integral":105},"maxPrice":{"value":"$94.49","integral":9449},"stores":30},"id":2442061740,"categoryId":8515},{"type":"PRODUCT","title":"Pho","description":"Snap on Ap","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":65194915004416.000000,"priceSet":{"minPrice":{"value":"$0.01","integral":1},"maxPrice":{"value":"$414.99","integral":41499},"stores":39},"id":2004746863,"categoryId":8515},{"type":"PRODUCT","title":"Otterbox i","description":"Your iPhon","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":61515478597632.000000,"priceSet":{"minPrice":{"value":"$3.28","integral":328},"maxPrice":{"value":"$110.65","integral":11065},"stores":25},"id":2584611575,"categoryId":8515}],"includedResults":10,"totalResults":2000}}, +{"classification":{"relevancyScore":1000,"searchUrl":{"value":"nix"}},"products":{"priceSet":{"minPrice":{"value":"$0.01","integral":1},"maxPrice":{"value":"$4,833.99","integral":483399}},"product":[{"type":"PRODUCT","title":"Silicone c","description":"Elite Hori","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":310711221747712.000000,"priceSet":{"minPrice":{"value":"$1.56","integral":156},"maxPrice":{"value":"$29.99","integral":2999},"stores":14},"id":1968262863,"categoryId":8515},{"type":"PRODUCT","title":"Nonslip Ch","description":"Specificat","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":175580930637824.000000,"priceSet":{"minPrice":{"value":"$0.45","integral":45},"maxPrice":{"value":"$194.95","integral":19495},"stores":34},"id":2534935499,"categoryId":8515},{"type":"PRODUCT","title":"Plastic Ca","description":"Descriptio","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":132488642953216.000000,"priceSet":{"minPrice":{"value":"$0.99","integral":99},"maxPrice":{"value":"$303.68","integral":30368},"stores":33},"id":2305624670,"categoryId":8515},{"type":"PRODUCT","title":"Protective","description":"Made of hi","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":108614681362432.000000,"priceSet":{"minPrice":{"value":"$1.70","integral":170},"maxPrice":{"value":"$99.99","integral":9999},"stores":11},"id":2120981405,"categoryId":8515},{"type":"PRODUCT","title":"P® 4","description":"Do more th","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":96203484168192.000000,"priceSet":{"minPrice":{"value":"$2.49","integral":249},"maxPrice":{"value":"$79.95","integral":7995},"stores":16},"id":2203798762,"categoryId":8515},{"type":"PRODUCT","title":"Case Refle","description":"NCAA iPhon","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":84727583211520.000000,"priceSet":{"minPrice":{"value":"$0.69","integral":69},"maxPrice":{"value":"$75.52","integral":7552},"stores":59},"id":1114627445,"categoryId":8515},{"type":"PRODUCT","title":"Infuse Pro","description":"Protect an","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":80831066406912.000000,"priceSet":{"minPrice":{"value":"$0.59","integral":59},"maxPrice":{"value":"$79.00","integral":7900},"stores":24},"id":2557462717,"categoryId":8515},{"type":"PRODUCT","title":"Dragonfly ","description":"d","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":70900229603328.000000,"priceSet":{"minPrice":{"value":"$1.05","integral":105},"maxPrice":{"value":"$94.49","integral":9449},"stores":30},"id":2442061740,"categoryId":8515},{"type":"PRODUCT","title":"Pho","description":"Snap on Ap","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":65194915004416.000000,"priceSet":{"minPrice":{"value":"$0.01","integral":1},"maxPrice":{"value":"$414.99","integral":41499},"stores":39},"id":2004746863,"categoryId":8515},{"type":"PRODUCT","title":"Otterbox i","description":"Your iPhon","manufacturer":"someone","url":{"value":"nix"},"images":{"image":[{"value":"nix","xsize":60,"ysize":60},{"value":"nix","xsize":100,"ysize":100},{"value":"nix","xsize":160,"ysize":160},{"value":"nix","xsize":400,"ysize":400}]},"relevancy":61515478597632.000000,"priceSet":{"minPrice":{"value":"$3.28","integral":328},"maxPrice":{"value":"$110.65","integral":11065},"stores":25},"id":2584611575,"categoryId":8515}],"includedResults":10,"totalResults":2000}} +] + diff --git a/tests/benchmarks/json/tst_bench_json.cpp b/tests/benchmarks/json/tst_bench_json.cpp new file mode 100644 index 00000000000..02481950641 --- /dev/null +++ b/tests/benchmarks/json/tst_bench_json.cpp @@ -0,0 +1,269 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include + +#include + +using namespace Json; + +class BenchmarkJson: public QObject +{ + Q_OBJECT + +public: + BenchmarkJson() {} + +private Q_SLOTS: + void jsonObjectInsertQt(); + void jsonObjectInsertStd(); + + void createBinaryMessageQt(); + void createBinaryMessageStd(); + + void readBinaryMessageQt(); + void readBinaryMessageStd(); + + void createTextMessageQt(); + void createTextMessageStd(); + + void readTextMessageQt(); + void readTextMessageStd(); + + void parseJsonQt(); + void parseJsonStd(); + + void parseNumbersQt(); + void parseNumbersStd(); +}; + +void BenchmarkJson::parseNumbersQt() +{ + QString testFile = QFINDTESTDATA("numbers.json"); + QVERIFY2(!testFile.isEmpty(), "cannot find test file numbers.json!"); + QFile file(testFile); + file.open(QFile::ReadOnly); + QByteArray testJson = file.readAll(); + + QBENCHMARK { + QJsonDocument doc = QJsonDocument::fromJson(testJson); + doc.object(); + } +} + +void BenchmarkJson::parseNumbersStd() +{ + QString testFile = QFINDTESTDATA("numbers.json"); + QVERIFY2(!testFile.isEmpty(), "cannot find test file numbers.json!"); + QFile file(testFile); + file.open(QFile::ReadOnly); + std::string testJson = file.readAll().toStdString(); + + QBENCHMARK { + JsonDocument doc = JsonDocument::fromJson(testJson); + doc.object(); + } +} + +void BenchmarkJson::parseJsonQt() +{ + QString testFile = QFINDTESTDATA("test.json"); + QVERIFY2(!testFile.isEmpty(), "cannot find test file test.json!"); + QFile file(testFile); + file.open(QFile::ReadOnly); + QByteArray testJson = file.readAll(); + + QBENCHMARK { + QJsonDocument doc = QJsonDocument::fromJson(testJson); + doc.object(); + } +} + +void BenchmarkJson::parseJsonStd() +{ + QString testFile = QFINDTESTDATA("test.json"); + QVERIFY2(!testFile.isEmpty(), "cannot find test file test.json!"); + QFile file(testFile); + file.open(QFile::ReadOnly); + std::string testJson = file.readAll().toStdString(); + + QBENCHMARK { + JsonDocument doc = JsonDocument::fromJson(testJson); + doc.object(); + } +} + +void BenchmarkJson::createBinaryMessageQt() +{ + // Example: send information over a datastream to another process + // Measure performance of creating and processing data into bytearray + QBENCHMARK { + QJsonObject ob; + ob.insert(QStringLiteral("command"), 1); + ob.insert(QStringLiteral("key"), "some information"); + ob.insert(QStringLiteral("env"), "some environment variables"); + QJsonDocument(ob).toBinaryData(); + } +} + +void BenchmarkJson::createBinaryMessageStd() +{ + // Example: send information over a datastream to another process + // Measure performance of creating and processing data into bytearray + QBENCHMARK { + JsonObject ob; + ob.insert("command", 1); + ob.insert("key", "some information"); + ob.insert("env", "some environment variables"); + JsonDocument(ob).toBinaryData(); + } +} + +void BenchmarkJson::readBinaryMessageQt() +{ + // Example: receive information over a datastream from another process + // Measure performance of converting content back to QVariantMap + // We need to recreate the bytearray but here we only want to measure the latter + QJsonObject ob; + ob.insert(QStringLiteral("command"), 1); + ob.insert(QStringLiteral("key"), "some information"); + ob.insert(QStringLiteral("env"), "some environment variables"); + QByteArray msg = QJsonDocument(ob).toBinaryData(); + + QBENCHMARK { + QJsonDocument::fromBinaryData(msg, QJsonDocument::Validate).object(); + } +} + +void BenchmarkJson::readBinaryMessageStd() +{ + // Example: receive information over a datastream from another process + // Measure performance of converting content back to QVariantMap + // We need to recreate the bytearray but here we only want to measure the latter + JsonObject ob; + ob.insert("command", 1); + ob.insert("key", "some information"); + ob.insert("env", "some environment variables"); + std::string msg = JsonDocument(ob).toBinaryData(); + + QBENCHMARK { + JsonDocument::fromBinaryData(msg, JsonDocument::Validate).object(); + } +} + +void BenchmarkJson::createTextMessageQt() +{ + // Example: send information over a datastream to another process + // Measure performance of creating and processing data into bytearray + QBENCHMARK { + QJsonObject ob; + ob.insert(QStringLiteral("command"), 1); + ob.insert(QStringLiteral("key"), "some information"); + ob.insert(QStringLiteral("env"), "some environment variables"); + QByteArray msg = QJsonDocument(ob).toJson(); + } +} + +void BenchmarkJson::createTextMessageStd() +{ + // Example: send information over a datastream to another process + // Measure performance of creating and processing data into bytearray + QBENCHMARK { + JsonObject ob; + ob.insert("command", 1); + ob.insert("key", "some information"); + ob.insert("env", "some environment variables"); + std::string msg = JsonDocument(ob).toJson(); + } +} + +void BenchmarkJson::readTextMessageQt() +{ + // Example: receive information over a datastream from another process + // Measure performance of converting content back to QVariantMap + // We need to recreate the bytearray but here we only want to measure the latter + QJsonObject ob; + ob.insert(QStringLiteral("command"), 1); + ob.insert(QStringLiteral("key"), "some information"); + ob.insert(QStringLiteral("env"), "some environment variables"); + QByteArray msg = QJsonDocument(ob).toJson(); + + QBENCHMARK { + QJsonDocument::fromJson(msg).object(); + } +} + +void BenchmarkJson::readTextMessageStd() +{ + // Example: receive information over a datastream from another process + // Measure performance of converting content back to QVariantMap + // We need to recreate the bytearray but here we only want to measure the latter + JsonObject ob; + ob.insert("command", 1); + ob.insert("key", "some information"); + ob.insert("env", "some environment variables"); + std::string msg = JsonDocument(ob).toJson(); + + QBENCHMARK { + JsonDocument::fromJson(msg).object(); + } +} + +void BenchmarkJson::jsonObjectInsertQt() +{ + QJsonObject object; + QJsonValue value(1.5); + + QBENCHMARK { + for (int i = 0; i < 1000; i++) + object.insert("testkey_" + QString::number(i), value); + } +} + +void BenchmarkJson::jsonObjectInsertStd() +{ + JsonObject object; + JsonValue value(1.5); + + QBENCHMARK { + for (int i = 0; i < 1000; i++) + object.insert("testkey_" + std::to_string(i), value); + } +} + +QTEST_MAIN(BenchmarkJson) + +#include "tst_bench_json.moc" + diff --git a/tests/unit/unittest/clangcodecompleteresultstest.cpp b/tests/unit/unittest/clangcodecompleteresultstest.cpp index dd7d3264514..c91c80ec932 100644 --- a/tests/unit/unittest/clangcodecompleteresultstest.cpp +++ b/tests/unit/unittest/clangcodecompleteresultstest.cpp @@ -58,6 +58,7 @@ TEST(ClangCodeCompleteResults, GetData) ClangBackEnd::TranslationUnits translationUnits{projects, unsavedFiles}; TranslationUnit translationUnit(Utf8StringLiteral(TESTDATA_DIR"/complete_testfile_1.cpp"), projectPart, + Utf8StringVector(), translationUnits); CXCodeCompleteResults *cxCodeCompleteResults = clang_codeCompleteAt(translationUnit.cxTranslationUnit(), translationUnit.filePath().constData(), 49, 1, 0, 0, 0); @@ -83,6 +84,7 @@ TEST(ClangCodeCompleteResults, MoveClangCodeCompleteResults) ClangBackEnd::TranslationUnits translationUnits{projects, unsavedFiles}; TranslationUnit translationUnit(Utf8StringLiteral(TESTDATA_DIR"/complete_testfile_1.cpp"), projectPart, + Utf8StringVector(), translationUnits); CXCodeCompleteResults *cxCodeCompleteResults = clang_codeCompleteAt(translationUnit.cxTranslationUnit(), translationUnit.filePath().constData(), 49, 1, 0, 0, 0); diff --git a/tests/unit/unittest/clangipcservertest.cpp b/tests/unit/unittest/clangipcservertest.cpp index 93182ab8d92..175e19e5213 100644 --- a/tests/unit/unittest/clangipcservertest.cpp +++ b/tests/unit/unittest/clangipcservertest.cpp @@ -259,7 +259,8 @@ TEST_F(ClangIpcServer, GetCodeCompletionForUnsavedFile) TEST_F(ClangIpcServer, GetNoCodeCompletionAfterRemovingUnsavedFile) { - clangServer.updateTranslationUnitsForEditor(UpdateTranslationUnitsForEditorMessage({FileContainer(functionTestFilePath, projectPartId, 74)})); + clangServer.updateTranslationUnitsForEditor(UpdateTranslationUnitsForEditorMessage( + {FileContainer(functionTestFilePath, projectPartId, Utf8StringVector(), 74)})); CompleteCodeMessage completeCodeMessage(functionTestFilePath, 20, 1, diff --git a/tests/unit/unittest/codecompletionsextractortest.cpp b/tests/unit/unittest/codecompletionsextractortest.cpp index c0e79f75057..42b5a6152b4 100644 --- a/tests/unit/unittest/codecompletionsextractortest.cpp +++ b/tests/unit/unittest/codecompletionsextractortest.cpp @@ -160,13 +160,13 @@ protected: ClangBackEnd::ProjectParts projects; ClangBackEnd::UnsavedFiles unsavedFiles; ClangBackEnd::TranslationUnits translationUnits{projects, unsavedFiles}; - TranslationUnit functionTranslationUnit{Utf8StringLiteral(TESTDATA_DIR"/complete_extractor_function.cpp"), project, translationUnits}; - TranslationUnit variableTranslationUnit{Utf8StringLiteral(TESTDATA_DIR"/complete_extractor_variable.cpp"), project, translationUnits}; - TranslationUnit classTranslationUnit{Utf8StringLiteral(TESTDATA_DIR"/complete_extractor_class.cpp"), project, translationUnits}; - TranslationUnit namespaceTranslationUnit{Utf8StringLiteral(TESTDATA_DIR"/complete_extractor_namespace.cpp"), project, translationUnits}; - TranslationUnit enumerationTranslationUnit{Utf8StringLiteral(TESTDATA_DIR"/complete_extractor_enumeration.cpp"), project, translationUnits}; - TranslationUnit constructorTranslationUnit{Utf8StringLiteral(TESTDATA_DIR"/complete_extractor_constructor.cpp"), project, translationUnits}; - TranslationUnit briefCommentTranslationUnit{Utf8StringLiteral(TESTDATA_DIR"/complete_extractor_brief_comment.cpp"), project, translationUnits}; + TranslationUnit functionTranslationUnit{Utf8StringLiteral(TESTDATA_DIR"/complete_extractor_function.cpp"), project, Utf8StringVector(), translationUnits}; + TranslationUnit variableTranslationUnit{Utf8StringLiteral(TESTDATA_DIR"/complete_extractor_variable.cpp"), project, Utf8StringVector(), translationUnits}; + TranslationUnit classTranslationUnit{Utf8StringLiteral(TESTDATA_DIR"/complete_extractor_class.cpp"), project, Utf8StringVector(), translationUnits}; + TranslationUnit namespaceTranslationUnit{Utf8StringLiteral(TESTDATA_DIR"/complete_extractor_namespace.cpp"), project, Utf8StringVector(), translationUnits}; + TranslationUnit enumerationTranslationUnit{Utf8StringLiteral(TESTDATA_DIR"/complete_extractor_enumeration.cpp"), project, Utf8StringVector(), translationUnits}; + TranslationUnit constructorTranslationUnit{Utf8StringLiteral(TESTDATA_DIR"/complete_extractor_constructor.cpp"), project, Utf8StringVector(), translationUnits}; + TranslationUnit briefCommentTranslationUnit{Utf8StringLiteral(TESTDATA_DIR"/complete_extractor_brief_comment.cpp"), project, Utf8StringVector(), translationUnits}; }; TEST_F(CodeCompletionsExtractor, Function) @@ -527,7 +527,7 @@ TEST_F(CodeCompletionsExtractor, NotAvailableFunction) TEST_F(CodeCompletionsExtractor, UnsavedFile) { - TranslationUnit translationUnit(Utf8String::fromUtf8(TESTDATA_DIR"/complete_extractor_function.cpp"), project, translationUnits); + TranslationUnit translationUnit(Utf8String::fromUtf8(TESTDATA_DIR"/complete_extractor_function.cpp"), project, Utf8StringVector(), translationUnits); unsavedFiles.createOrUpdate({unsavedDataFileContainer(TESTDATA_DIR"/complete_extractor_function.cpp", TESTDATA_DIR"/complete_extractor_function_unsaved.cpp")}); ClangCodeCompleteResults completeResults(getResults(translationUnit, 20)); @@ -542,7 +542,7 @@ TEST_F(CodeCompletionsExtractor, UnsavedFile) TEST_F(CodeCompletionsExtractor, ChangeUnsavedFile) { - TranslationUnit translationUnit(Utf8String::fromUtf8(TESTDATA_DIR"/complete_extractor_function.cpp"), project, translationUnits); + TranslationUnit translationUnit(Utf8String::fromUtf8(TESTDATA_DIR"/complete_extractor_function.cpp"), project, Utf8StringVector(), translationUnits); unsavedFiles.createOrUpdate({unsavedDataFileContainer(TESTDATA_DIR"/complete_extractor_function.cpp", TESTDATA_DIR"/complete_extractor_function_unsaved.cpp")}); ClangCodeCompleteResults completeResults(getResults(translationUnit, 20)); diff --git a/tests/unit/unittest/data/diagnostic_source_range.cpp b/tests/unit/unittest/data/diagnostic_source_range.cpp index ae0ee72f593..121d402ea89 100644 --- a/tests/unit/unittest/data/diagnostic_source_range.cpp +++ b/tests/unit/unittest/data/diagnostic_source_range.cpp @@ -7,3 +7,7 @@ int function(XXX i) { i + 20; } + +struct Foo { + someIdentifierLeadingToInvalidRange; +}; diff --git a/tests/unit/unittest/diagnosticsettest.cpp b/tests/unit/unittest/diagnosticsettest.cpp index 3949bbcbc88..6b5da58a10f 100644 --- a/tests/unit/unittest/diagnosticsettest.cpp +++ b/tests/unit/unittest/diagnosticsettest.cpp @@ -58,6 +58,7 @@ protected: ClangBackEnd::TranslationUnits translationUnits{projects, unsavedFiles}; TranslationUnit translationUnit{Utf8StringLiteral(TESTDATA_DIR"/diagnostic_diagnosticset.cpp"), projectPart, + Utf8StringVector(), translationUnits}; }; diff --git a/tests/unit/unittest/diagnostictest.cpp b/tests/unit/unittest/diagnostictest.cpp index 9ad2fbecdc9..11294493107 100644 --- a/tests/unit/unittest/diagnostictest.cpp +++ b/tests/unit/unittest/diagnostictest.cpp @@ -83,6 +83,7 @@ protected: ClangBackEnd::TranslationUnits translationUnits{projects, unsavedFiles}; TranslationUnit translationUnit{Utf8StringLiteral(TESTDATA_DIR"/diagnostic_diagnostic.cpp"), projectPart, + Utf8StringVector(), translationUnits}; DiagnosticSet diagnosticSet{translationUnit.diagnostics()}; ::Diagnostic diagnostic{diagnosticSet.back()}; diff --git a/tests/unit/unittest/fixittest.cpp b/tests/unit/unittest/fixittest.cpp index e5327f6279f..6942bf6e323 100644 --- a/tests/unit/unittest/fixittest.cpp +++ b/tests/unit/unittest/fixittest.cpp @@ -82,6 +82,7 @@ protected: ClangBackEnd::TranslationUnits translationUnits{projects, unsavedFiles}; TranslationUnit translationUnit{Utf8StringLiteral(TESTDATA_DIR"/diagnostic_semicolon_fixit.cpp"), projectPart, + Utf8StringVector(), translationUnits}; DiagnosticSet diagnosticSet{translationUnit.diagnostics()}; Diagnostic diagnostic{diagnosticSet.front()}; diff --git a/tests/unit/unittest/sourcelocationtest.cpp b/tests/unit/unittest/sourcelocationtest.cpp index d3b5a2deb61..9db922c282b 100644 --- a/tests/unit/unittest/sourcelocationtest.cpp +++ b/tests/unit/unittest/sourcelocationtest.cpp @@ -63,6 +63,7 @@ protected: ClangBackEnd::TranslationUnits translationUnits{projects, unsavedFiles}; TranslationUnit translationUnit{Utf8StringLiteral(TESTDATA_DIR"/diagnostic_source_location.cpp"), projectPart, + Utf8StringVector(), translationUnits}; DiagnosticSet diagnosticSet{translationUnit.diagnostics()}; Diagnostic diagnostic{diagnosticSet.front()}; diff --git a/tests/unit/unittest/sourcerangetest.cpp b/tests/unit/unittest/sourcerangetest.cpp index 00a57d5d247..771bd49ccff 100644 --- a/tests/unit/unittest/sourcerangetest.cpp +++ b/tests/unit/unittest/sourcerangetest.cpp @@ -82,12 +82,28 @@ protected: ClangBackEnd::TranslationUnits translationUnits{projects, unsavedFiles}; TranslationUnit translationUnit{Utf8StringLiteral(TESTDATA_DIR"/diagnostic_source_range.cpp"), projectPart, + Utf8StringVector(), translationUnits}; DiagnosticSet diagnosticSet{translationUnit.diagnostics()}; Diagnostic diagnostic{diagnosticSet.front()}; + Diagnostic diagnosticWithFilteredOutInvalidRange{diagnosticSet.at(1)}; ::SourceRange sourceRange{diagnostic.ranges().front()}; }; +TEST_F(SourceRange, IsNull) +{ + ::SourceRange sourceRange; + + ASSERT_TRUE(sourceRange.isNull()); +} + +TEST_F(SourceRange, IsNotNull) +{ + ::SourceRange sourceRange = diagnostic.ranges()[0]; + + ASSERT_FALSE(sourceRange.isNull()); +} + TEST_F(SourceRange, Size) { ASSERT_THAT(diagnostic.ranges().size(), 2); @@ -108,4 +124,10 @@ TEST_F(SourceRange, End) 6u, 44u)); } + +TEST_F(SourceRange, InvalidRangeIsFilteredOut) +{ + ASSERT_TRUE(diagnosticWithFilteredOutInvalidRange.ranges().empty()); +} + } diff --git a/tests/unit/unittest/translationunitstest.cpp b/tests/unit/unittest/translationunitstest.cpp index d291c368b09..ec2dfb9951d 100644 --- a/tests/unit/unittest/translationunitstest.cpp +++ b/tests/unit/unittest/translationunitstest.cpp @@ -129,7 +129,7 @@ TEST_F(TranslationUnits, DoNotThrowForAddingNonExistingFileWithUnsavedContent) TEST_F(TranslationUnits, Add) { - ClangBackEnd::FileContainer fileContainer(filePath, projectPartId, 74u); + ClangBackEnd::FileContainer fileContainer(filePath, projectPartId, Utf8StringVector(), 74u); translationUnits.create({fileContainer}); @@ -139,7 +139,7 @@ TEST_F(TranslationUnits, Add) TEST_F(TranslationUnits, ThrowForCreatingAnExistingTranslationUnit) { - ClangBackEnd::FileContainer fileContainer(filePath, projectPartId, 74u); + ClangBackEnd::FileContainer fileContainer(filePath, projectPartId, Utf8StringVector(), 74u); translationUnits.create({fileContainer}); ASSERT_THROW(translationUnits.create({fileContainer}), @@ -148,15 +148,15 @@ TEST_F(TranslationUnits, ThrowForCreatingAnExistingTranslationUnit) TEST_F(TranslationUnits, ThrowForUpdatingANonExistingTranslationUnit) { - ClangBackEnd::FileContainer fileContainer(filePath, projectPartId, 74u); + ClangBackEnd::FileContainer fileContainer(filePath, projectPartId, Utf8StringVector(), 74u); ASSERT_THROW(translationUnits.update({fileContainer}), ClangBackEnd::TranslationUnitDoesNotExistException); } TEST_F(TranslationUnits, Update) { - ClangBackEnd::FileContainer createFileContainer(filePath, projectPartId, 74u); - ClangBackEnd::FileContainer updateFileContainer(filePath, Utf8String(), 75u); + ClangBackEnd::FileContainer createFileContainer(filePath, projectPartId, Utf8StringVector(), 74u); + ClangBackEnd::FileContainer updateFileContainer(filePath, Utf8String(), Utf8StringVector(), 75u); translationUnits.create({createFileContainer}); translationUnits.update({updateFileContainer}); @@ -167,8 +167,8 @@ TEST_F(TranslationUnits, Update) TEST_F(TranslationUnits, UpdateUnsavedFileAndCheckForReparse) { - ClangBackEnd::FileContainer fileContainer(filePath, projectPartId, 74u); - ClangBackEnd::FileContainer headerContainer(headerPath, projectPartId, 74u); + ClangBackEnd::FileContainer fileContainer(filePath, projectPartId, Utf8StringVector(), 74u); + ClangBackEnd::FileContainer headerContainer(headerPath, projectPartId, Utf8StringVector(), 74u); ClangBackEnd::FileContainer headerContainerWithUnsavedContent(headerPath, projectPartId, Utf8String(), true, 75u); translationUnits.create({fileContainer, headerContainer}); translationUnits.translationUnit(filePath, projectPartId).cxTranslationUnit(); @@ -180,8 +180,8 @@ TEST_F(TranslationUnits, UpdateUnsavedFileAndCheckForReparse) TEST_F(TranslationUnits, UpdateUnsavedFileAndCheckForDiagnostics) { - ClangBackEnd::FileContainer fileContainer(filePath, projectPartId, 74u); - ClangBackEnd::FileContainer headerContainer(headerPath, projectPartId, 74u); + ClangBackEnd::FileContainer fileContainer(filePath, projectPartId, Utf8StringVector(), 74u); + ClangBackEnd::FileContainer headerContainer(headerPath, projectPartId, Utf8StringVector(), 74u); ClangBackEnd::FileContainer headerContainerWithUnsavedContent(headerPath, projectPartId, Utf8String(), true, 75u); translationUnits.create({fileContainer, headerContainer}); translationUnits.translationUnit(filePath, projectPartId).diagnostics(); @@ -193,8 +193,8 @@ TEST_F(TranslationUnits, UpdateUnsavedFileAndCheckForDiagnostics) TEST_F(TranslationUnits, RemoveFileAndCheckForDiagnostics) { - ClangBackEnd::FileContainer fileContainer(filePath, projectPartId, 74u); - ClangBackEnd::FileContainer headerContainer(headerPath, projectPartId, 74u); + ClangBackEnd::FileContainer fileContainer(filePath, projectPartId, Utf8StringVector(), 74u); + ClangBackEnd::FileContainer headerContainer(headerPath, projectPartId, Utf8StringVector(), 74u); ClangBackEnd::FileContainer headerContainerWithUnsavedContent(headerPath, projectPartId, Utf8String(), true, 75u); translationUnits.create({fileContainer, headerContainer}); translationUnits.translationUnit(filePath, projectPartId).diagnostics(); @@ -206,7 +206,7 @@ TEST_F(TranslationUnits, RemoveFileAndCheckForDiagnostics) TEST_F(TranslationUnits, DontGetNewerFileContainerIfRevisionIsTheSame) { - ClangBackEnd::FileContainer fileContainer(filePath, projectPartId, 74u); + ClangBackEnd::FileContainer fileContainer(filePath, projectPartId, Utf8StringVector(), 74u); translationUnits.create({fileContainer}); auto newerFileContainers = translationUnits.newerFileContainers({fileContainer}); @@ -216,8 +216,8 @@ TEST_F(TranslationUnits, DontGetNewerFileContainerIfRevisionIsTheSame) TEST_F(TranslationUnits, GetNewerFileContainerIfRevisionIsDifferent) { - ClangBackEnd::FileContainer fileContainer(filePath, projectPartId, 74u); - ClangBackEnd::FileContainer newerContainer(filePath, projectPartId, 75u); + ClangBackEnd::FileContainer fileContainer(filePath, projectPartId, Utf8StringVector(), 74u); + ClangBackEnd::FileContainer newerContainer(filePath, projectPartId, Utf8StringVector(), 75u); translationUnits.create({fileContainer}); auto newerFileContainers = translationUnits.newerFileContainers({newerContainer}); @@ -263,6 +263,7 @@ TEST_F(TranslationUnits, RemoveAllValidIfExceptionIsThrown) ASSERT_THAT(translationUnits.translationUnits(), Not(Contains(TranslationUnit(filePath, projects.project(projectPartId), + Utf8StringVector(), translationUnits)))); } diff --git a/tests/unit/unittest/translationunittest.cpp b/tests/unit/unittest/translationunittest.cpp index f03ff889991..452aa171aec 100644 --- a/tests/unit/unittest/translationunittest.cpp +++ b/tests/unit/unittest/translationunittest.cpp @@ -28,6 +28,7 @@ ** ****************************************************************************/ +#include #include #include #include @@ -61,6 +62,7 @@ using ClangBackEnd::TranslationUnits; using testing::IsNull; using testing::NotNull; +using testing::Eq; using testing::Gt; using testing::Contains; using testing::EndsWith; @@ -82,6 +84,7 @@ protected: ClangBackEnd::TranslationUnits translationUnits{projects, unsavedFiles}; ::TranslationUnit translationUnit{translationUnitFilePath, projectPart, + Utf8StringVector(), translationUnits}; }; @@ -94,13 +97,13 @@ TEST_F(TranslationUnit, DefaultTranslationUnitIsInvalid) TEST_F(TranslationUnit, ThrowExceptionForNonExistingFilePath) { - ASSERT_THROW(::TranslationUnit(Utf8StringLiteral("file.cpp"), projectPart, translationUnits), + ASSERT_THROW(::TranslationUnit(Utf8StringLiteral("file.cpp"), projectPart, Utf8StringVector(), translationUnits), ClangBackEnd::TranslationUnitFileNotExitsException); } TEST_F(TranslationUnit, ThrowNoExceptionForNonExistingFilePathIfDoNotCheckIfFileExistsIsSet) { - ASSERT_NO_THROW(::TranslationUnit(Utf8StringLiteral("file.cpp"), projectPart, translationUnits, ::TranslationUnit::DoNotCheckIfFileExists)); + ASSERT_NO_THROW(::TranslationUnit(Utf8StringLiteral("file.cpp"), projectPart, Utf8StringVector(), translationUnits, ::TranslationUnit::DoNotCheckIfFileExists)); } TEST_F(TranslationUnit, TranslationUnitIsValid) @@ -147,6 +150,13 @@ TEST_F(TranslationUnit, ResetedTranslationUnitIsNull) ASSERT_TRUE(translationUnit.isNull()); } +TEST_F(TranslationUnit, LastCommandLineArgumentIsFilePath) +{ + const auto arguments = translationUnit.commandLineArguments(); + + ASSERT_THAT(arguments.at(arguments.count() - 1), Eq(translationUnitFilePath)); +} + TEST_F(TranslationUnit, TimeStampForProjectPartChangeIsUpdatedAsNewCxTranslationUnitIsGenerated) { auto lastChangeTimePoint = translationUnit.lastProjectPartChangeTimePoint(); @@ -274,6 +284,7 @@ TEST_F(TranslationUnit, DeletedFileShouldBeNotSetDirty) EXPECT_TRUE(temporaryFile.write(readContentFromTranslationUnitFile())); ::TranslationUnit translationUnit(temporaryFile.fileName(), projectPart, + Utf8StringVector(), translationUnits); return translationUnit;