diff options
author | Eike Ziller <eike.ziller@theqtcompany.com> | 2015-07-10 14:08:29 +0200 |
---|---|---|
committer | Eike Ziller <eike.ziller@theqtcompany.com> | 2015-07-10 14:08:29 +0200 |
commit | d6da70916d566bec517aedeeae3f224b8d0489a1 (patch) | |
tree | 55c20439f5c65a81647eb7999a25c472d20c7fc5 | |
parent | c69d3ded6b67b5501b9caf0d6f29148f389007ad (diff) | |
parent | a32a9b3d2a82d38edde1b4b55cbdececd7f5333d (diff) | |
download | qt-creator-d6da70916d566bec517aedeeae3f224b8d0489a1.tar.gz |
Merge remote-tracking branch 'origin/3.5'
84 files changed, 2736 insertions, 4697 deletions
diff --git a/doc/images/qmldesigner-new-project.png b/doc/images/qmldesigner-new-project.png Binary files differindex 40baa55739..1619fd18b3 100644 --- a/doc/images/qmldesigner-new-project.png +++ b/doc/images/qmldesigner-new-project.png diff --git a/doc/images/qtcreator-keyboard-shortcuts.png b/doc/images/qtcreator-keyboard-shortcuts.png Binary files differindex 514c17eb8a..064ba04fa8 100644 --- a/doc/images/qtcreator-keyboard-shortcuts.png +++ b/doc/images/qtcreator-keyboard-shortcuts.png diff --git a/doc/images/qtcreator-new-qt-quick-project-wizard.png b/doc/images/qtcreator-new-qt-quick-project-wizard.png Binary files differindex 259e2fd83e..5a676cd42d 100644 --- a/doc/images/qtcreator-new-qt-quick-project-wizard.png +++ b/doc/images/qtcreator-new-qt-quick-project-wizard.png diff --git a/doc/src/editors/creator-editors.qdoc b/doc/src/editors/creator-editors.qdoc index 124be5bb69..22e93118fb 100644 --- a/doc/src/editors/creator-editors.qdoc +++ b/doc/src/editors/creator-editors.qdoc @@ -1466,6 +1466,11 @@ \endlist + The locations of search hits, breakpoints, and bookmarks in your document + are highlighted on the editor scroll bar. To turn highlighting off, select + \uicontrol Tools > \uicontrol Options > \uicontrol {Text Editor} > + \uicontrol {Highlight search results on the scrollbar}. + \section1 Advanced Search To search through projects, files on a file system or currently open files: @@ -2403,9 +2408,10 @@ \endlist - To move directly to a particular line in the document when you open the - document, append a plus sign (+) or a colon (:) to the file name in the - locator. For example, to open main.cpp to line 41, enter: \c {main.cpp:41}. + To move directly to a particular line and column in the document when you + open the document, append them to the file name in the locator, separated by + plus signs (+) or colons (:). For example, to open main.cpp to line 41 and + column 2, enter: \c {main.cpp:41:2}. If the path to a file is very long, it might not fit into the locator window. To view the full path, press \key Alt when the filename is selected @@ -2446,7 +2452,7 @@ \li Locating symbols in the current document (\c {.}) \li Locating a specific line and column in the document displayed in - your editor (\c {l}) + your editor (\c {l <line_number>:<column_number>}) \li Opening help topics, including Qt documentation (\c {?}) @@ -2460,6 +2466,8 @@ \li Executing version control system commands (\c {git}). For more information, see \l{Using Version Control Systems} + \li Running external tools (\c x) + \endlist To use a specific locator filter, type the assigned prefix followed by diff --git a/doc/src/howto/creator-cli.qdoc b/doc/src/howto/creator-cli.qdoc index 194d29f0fe..392621642f 100644 --- a/doc/src/howto/creator-cli.qdoc +++ b/doc/src/howto/creator-cli.qdoc @@ -31,16 +31,16 @@ \title Using Command Line Options You can start \QC and specify some options from the command line. For - example, you can open a file to any line. + example, you can open a file to any line and column. To specify command line options, enter the following command in the \QC installation or build directory: - \c {qtcreator [option] [filename[:line_number]]} + \c {qtcreator [option] [filename[:line_number[:column_number]]]} \note You can use either a colon (:) or a plus sign (+) as a separator - between the filename and line number. You can also use a space between the - separator and the line number. + between the filename and line number and the line number and the column + number. You can also use a space between the separator and the line number. For example, on Windows: @@ -48,9 +48,9 @@ \li \c {C:\qtcreator\bin>qtcreator -help} - \li \c {C:\qtcreator\bin>qtcreator C:\TextFinder\textfinder.cpp:100} + \li \c {C:\qtcreator\bin>qtcreator C:\TextFinder\textfinder.cpp:100:2} - \li \c {C:\qtcreator\bin>qtcreator C:\TextFinder\textfinder.cpp +100} + \li \c {C:\qtcreator\bin>qtcreator C:\TextFinder\textfinder.cpp +100+2} \endlist diff --git a/doc/src/howto/creator-external-tools.qdoc b/doc/src/howto/creator-external-tools.qdoc index c507749c3f..67bf9c2276 100644 --- a/doc/src/howto/creator-external-tools.qdoc +++ b/doc/src/howto/creator-external-tools.qdoc @@ -36,6 +36,9 @@ for use. You can change their default configurations and configure new tools. + To run the tools, select \uicontrol Tools > \uicontrol External, or use the + \c x filter in the locator. + \section1 Using Qt Linguist You can use the Qt Linguist release manager tools, lupdate and lrelease, diff --git a/doc/src/howto/creator-keyboard-shortcuts.qdoc b/doc/src/howto/creator-keyboard-shortcuts.qdoc index aae209af9c..8c2a0323b9 100644 --- a/doc/src/howto/creator-keyboard-shortcuts.qdoc +++ b/doc/src/howto/creator-keyboard-shortcuts.qdoc @@ -64,8 +64,19 @@ \li Select a command from the list. - \li In \uicontrol{Key Sequence} enter the shortcut key you want to associate - with the selected command. + \li In the \uicontrol{Key Sequence} field, you have the following + options: + + \list + + \li Enter the shortcut key you want to associate with the + selected command. + + \li Select \uicontrol Record, press the keys to use as the + keyboard shortcut, and select \uicontrol {Stop Recording} + when you are done. + + \endlist \li To revert to the default shortcut, select \uicontrol Reset. diff --git a/doc/src/projects/creator-projects-creating.qdoc b/doc/src/projects/creator-projects-creating.qdoc index 2d9b680330..bc343c836b 100644 --- a/doc/src/projects/creator-projects-creating.qdoc +++ b/doc/src/projects/creator-projects-creating.qdoc @@ -114,6 +114,12 @@ Create a Qt Quick application using Qt Quick Controls + \li Qt Canvas 3D Application + + Create a Qt Quick application that imports the Qt Canvas 3D + module and, optionally, includes \l{http://threejs.org} + {three.js}. + \li Qt Console Application Use a single main.cpp file diff --git a/doc/src/qtquick/qtquick-creating.qdoc b/doc/src/qtquick/qtquick-creating.qdoc index 4e20650988..ea861f5b18 100644 --- a/doc/src/qtquick/qtquick-creating.qdoc +++ b/doc/src/qtquick/qtquick-creating.qdoc @@ -46,6 +46,10 @@ \li \uicontrol {Qt Quick Controls Application} is like \uicontrol {Qt Quick Application}, but using Qt Quick Controls. + \li \uicontrol {Qt Canvas 3D Application} creates a Qt Quick application + that imports the Qt Canvas 3D module and, optionally, includes + \l{http://threejs.org}{three.js}. + \li \uicontrol {Qt Quick UI} (in the \uicontrol {Other Project} category) creates a Qt Quick UI project with a single QML file that contains the main view. You can review Qt Quick UI projects in a diff --git a/qtcreator.pri b/qtcreator.pri index f68fa435d5..71e7d4477e 100644 --- a/qtcreator.pri +++ b/qtcreator.pri @@ -1,9 +1,9 @@ !isEmpty(QTCREATOR_PRI_INCLUDED):error("qtcreator.pri already included") QTCREATOR_PRI_INCLUDED = 1 -QTCREATOR_VERSION = 3.4.81 -QTCREATOR_COMPAT_VERSION = 3.4.81 -BINARY_ARTIFACTS_BRANCH = master +QTCREATOR_VERSION = 3.4.82 +QTCREATOR_COMPAT_VERSION = 3.4.82 +BINARY_ARTIFACTS_BRANCH = 3.5 # enable c++11 CONFIG += c++11 diff --git a/qtcreator.qbs b/qtcreator.qbs index b2dd649476..e9295856bb 100644 --- a/qtcreator.qbs +++ b/qtcreator.qbs @@ -6,11 +6,11 @@ Project { property bool withAutotests: qbs.buildVariant === "debug" property string ide_version_major: '3' property string ide_version_minor: '4' - property string ide_version_release: '81' + property string ide_version_release: '82' property string qtcreator_version: ide_version_major + '.' + ide_version_minor + '.' + ide_version_release property string ide_compat_version_major: '3' property string ide_compat_version_minor: '4' - property string ide_compat_version_release: '81' + property string ide_compat_version_release: '82' property string qtcreator_compat_version: ide_compat_version_major + '.' + ide_compat_version_minor + '.' + ide_compat_version_release property path ide_source_tree: path property string ide_app_path: qbs.targetOS.contains("osx") ? "" : "bin" diff --git a/scripts/deployqt.py b/scripts/deployqt.py index 23d2074785..00b70d1af4 100755 --- a/scripts/deployqt.py +++ b/scripts/deployqt.py @@ -191,7 +191,9 @@ def copy_qt_libs(install_dir, qt_libs_dir, qt_plugin_dir, qt_import_dir, qt_qml_ target = os.path.join(install_dir, 'bin', 'imports', qtimport) if (os.path.exists(target)): shutil.rmtree(target) - shutil.copytree(os.path.join(qt_import_dir, qtimport), target, ignore=copy_ignore_func, symlinks=True) + import_path = os.path.join(qt_import_dir, qtimport) + if os.path.exists(import_path): + shutil.copytree(import_path, target, ignore=copy_ignore_func, symlinks=True) if (os.path.exists(qt_qml_dir)): print "Copying qt quick 2 imports" @@ -230,20 +232,33 @@ def copyPreservingLinks(source, destination): shutil.copy(source, destination) def copy_libclang(install_dir, llvm_install_dir): - libsources = [] - libtarget = "" + # contains pairs of (source, target directory) + deployinfo = [] if sys.platform.startswith("win"): - libsources = [os.path.join(llvm_install_dir, 'bin', 'libclang.dll')] - libtarget = os.path.join(install_dir, 'bin') + deployinfo.append((os.path.join(llvm_install_dir, 'bin', 'libclang.dll'), + os.path.join(install_dir, 'bin'))) + deployinfo.append((os.path.join(llvm_install_dir, 'bin', 'clang-cl.exe'), + os.path.join(install_dir, 'bin'))) else: libsources = glob(os.path.join(llvm_install_dir, 'lib', 'libclang.so*')) - libtarget = os.path.join(install_dir, 'lib', 'qtcreator') + for libsource in libsources: + deployinfo.append((libsource, os.path.join(install_dir, 'lib', 'qtcreator'))) + clangbinary = os.path.join(llvm_install_dir, 'bin', 'clang') + clangbinary_targetdir = os.path.join(install_dir, 'bin') + deployinfo.append((clangbinary, clangbinary_targetdir)) + # copy link target if clang is actually a symlink + if os.path.islink(clangbinary): + linktarget = os.readlink(clangbinary) + deployinfo.append((os.path.join(os.path.dirname(clangbinary), linktarget), + os.path.join(clangbinary_targetdir, linktarget))) + resourcesource = os.path.join(llvm_install_dir, 'lib', 'clang') resourcetarget = os.path.join(install_dir, 'share', 'qtcreator', 'cplusplus', 'clang') + print "copying libclang..." - for libsource in libsources: - print libsource, '->', libtarget - copyPreservingLinks(libsource, libtarget) + for source, target in deployinfo: + print source, '->', target + copyPreservingLinks(source, target) print resourcesource, '->', resourcetarget if (os.path.exists(resourcetarget)): shutil.rmtree(resourcetarget) diff --git a/scripts/deployqtHelper_mac.sh b/scripts/deployqtHelper_mac.sh index 120a4d030a..be7929a5ff 100755 --- a/scripts/deployqtHelper_mac.sh +++ b/scripts/deployqtHelper_mac.sh @@ -61,6 +61,12 @@ if [ $LLVM_INSTALL_DIR ]; then # use recursive copy to make it copy symlinks as symlinks cp -Rf "$LLVM_INSTALL_DIR"/lib/libclang.*dylib "$1/Contents/Frameworks/" || exit 1 cp -Rf "$LLVM_INSTALL_DIR"/lib/clang "$1/Contents/Resources/cplusplus/" || exit 1 + clangsource="$LLVM_INSTALL_DIR"/bin/clang + clanglinktarget="$(readlink "$clangsource")" + cp -Rf "$clangsource" "$1/Contents/Resources/" || exit 1 + if [ $clanglinktarget ]; then + cp -Rf "$(dirname "$clangsource")/$clanglinktarget" "$1/Contents/Resources/$clanglinktarget" || exit 1 + fi fi _CLANG_CODEMODEL_LIB="$1/Contents/PlugIns/libClangCodeModel_debug.dylib" if [ ! -f "$_CLANG_CODEMODEL_LIB" ]; then diff --git a/scripts/unixdeployqt.sh b/scripts/unixdeployqt.sh deleted file mode 100755 index be843f3e30..0000000000 --- a/scripts/unixdeployqt.sh +++ /dev/null @@ -1,100 +0,0 @@ -#!/bin/bash -################################################################################ -# Copyright (C) 2015 The Qt Company Ltd -# All rights reserved. -# -# 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. -################################################################################ - -if [ $# -lt 1 ]; then - echo "Usage: $(basename $1) <creator_install_dir> [qmake_path]" - exit 1 -fi - -INSTALL_DIR="$1" -QMAKE_BIN="${2:-$(which qmake)}" - -if [ ! -e "$QMAKE_BIN" ]; then - echo "Could not detetermine location of 'qmake'!" - exit 1; -fi - -CHRPATH=$(which chrpath) -if [ ! -e "$CHRPATH" ]; then - echo "Cannot find required binary 'chrpath'." - exit 1 -fi - -QT_INSTALL_LIBS="$($QMAKE_BIN -query QT_INSTALL_LIBS)" -QT_INSTALL_PLUGINS="$($QMAKE_BIN -query QT_INSTALL_PLUGINS)" -QT_INSTALL_IMPORTS="$($QMAKE_BIN -query QT_INSTALL_IMPORTS)" -QT_INSTALL_TRANSLATIONS="$($QMAKE_BIN -query QT_INSTALL_TRANSLATIONS)" - -plugins="accessible designer iconengines imageformats sqldrivers" -imports="Qt QtWebKit" -tr_catalogs="assistant designer qt qt_help" -tr_languages="$(cd $INSTALL_DIR/share/qtcreator/translations; echo qtcreator_* | sed -e 's,[^_]*_\([^.]*\)\.,\1 ,g')" - -function fix_rpaths() -{ - pushd $INSTALL_DIR/lib - find qtcreator/ -maxdepth 1 -name "*.so*" -type f -exec $CHRPATH -r \$ORIGIN {} \; - cd $INSTALL_DIR/lib/qtcreator - find plugins/ -maxdepth 2 -name "*.so" -type f -exec $CHRPATH -r \$ORIGIN/../.. {} \; - - cd $INSTALL_DIR/bin - # all executable files in bin - find -maxdepth 1 -type f -executable -exec $CHRPATH -r \$ORIGIN/../lib/qtcreator {} \; - # all lib of imports and plugins one level underneath bin - find -mindepth 2 -maxdepth 2 -type f -name "*.so" -exec $CHRPATH -r \$ORIGIN/../../lib/qtcreator {} \; - find -mindepth 3 -maxdepth 3 -type f -name "*.so" -exec $CHRPATH -r \$ORIGIN/../../../lib/qtcreator {} \; - find -mindepth 4 -maxdepth 4 -type f -name "*.so" -exec $CHRPATH -r \$ORIGIN/../../../../lib/qtcreator {} \; -} - -function copy_binaries() -{ - cp -a $QT_INSTALL_LIBS/*.so* $INSTALL_DIR/lib/qtcreator - - for plugin in $plugins; do - cp -a $QT_INSTALL_PLUGINS/$plugin $INSTALL_DIR/bin - done - - for import in $imports; do - cp -a $QT_INSTALL_IMPORTS/$import $INSTALL_DIR/bin - done -} - -function copy_translations() -{ - for language in $tr_languages; do - for catalog in $tr_catalogs; do - cp -a $QT_INSTALL_TRANSLATIONS/${catalog}_${language}.qm $INSTALL_DIR/share/qtcreator/translations - done - done -} - -copy_binaries -copy_translations -fix_rpaths - diff --git a/share/qtcreator/debugger/dumper.py b/share/qtcreator/debugger/dumper.py index 28c2c4c2e0..16682a47c7 100644 --- a/share/qtcreator/debugger/dumper.py +++ b/share/qtcreator/debugger/dumper.py @@ -387,12 +387,27 @@ class DumperBase: self.isCli = False # Later set, or not set: - # cachedQtVersion self.stringCutOff = 10000 self.displayStringLimit = 100 + self.resetCaches() + + self.childrenPrefix = 'children=[' + self.childrenSuffix = '],' + + self.dumpermodules = [ + "qttypes", + "stdtypes", + "misctypes", + "boosttypes", + "creatortypes", + "personaltypes", + ] + + + def resetCaches(self): # This is a cache mapping from 'type name' to 'display alternatives'. - self.qqFormats = {} + self.qqFormats = { "QVariant (QVariantMap)" : mapForms() } # This is a cache of all known dumpers. self.qqDumpers = {} @@ -407,18 +422,6 @@ class DumperBase: # to not be QObject derived, it contains a 0 value. self.knownStaticMetaObjects = {} - self.childrenPrefix = 'children=[' - self.childrenSuffix = '],' - - self.dumpermodules = [ - "qttypes", - "stdtypes", - "misctypes", - "boosttypes", - "creatortypes", - "personaltypes", - ] - def putNewline(self): pass @@ -1674,10 +1677,7 @@ class DumperBase: pass def setupDumpers(self, _ = {}): - self.qqDumpers = {} - self.qqFormats = {} - self.qqEditable = {} - self.typeCache = {} + self.resetCaches() for mod in self.dumpermodules: m = importlib.import_module(mod) diff --git a/share/qtcreator/debugger/gdbbridge.py b/share/qtcreator/debugger/gdbbridge.py index 95d3c1f26d..088a618b0c 100644 --- a/share/qtcreator/debugger/gdbbridge.py +++ b/share/qtcreator/debugger/gdbbridge.py @@ -144,6 +144,7 @@ ScanStackCommand() class PlainDumper: def __init__(self, printer): self.printer = printer + self.typeCache = {} def __call__(self, d, value): printer = self.printer.invoke(value) @@ -223,6 +224,7 @@ class Dumper(DumperBase): # These values will be kept between calls to 'showData'. self.isGdb = True self.childEventAddress = None + self.typeCache = {} self.typesReported = {} self.typesToReport = {} self.qtNamespaceToReport = None diff --git a/share/qtcreator/debugger/qttypes.py b/share/qtcreator/debugger/qttypes.py index 268da78f71..90a256f484 100644 --- a/share/qtcreator/debugger/qttypes.py +++ b/share/qtcreator/debugger/qttypes.py @@ -1031,6 +1031,13 @@ def qdump__QMultiMap(d, value): qdump__QMap(d, value) +def qform__QVariantMap(): + return mapForms() + +def qdump__QVariantMap(d, value): + qdump__QMap(d, value) + + def qdump__QMetaObjectPrivate(d, value): d.putEmptyValue() d.putNumChild(1) @@ -1899,6 +1906,16 @@ def qdump__QUrl(d, value): d.putGenericItem("fragment", stringType, fragment, Hex4EncodedLittleEndian) d.putFields(value) + +def qdump__QUuid(d, value): + v = value["data4"] + d.putValue("{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}" + % (value["data1"], value["data2"], value["data3"], + v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7])) + d.putNumChild(1) + d.putPlainChildren(value) + + def qdumpHelper_QVariant_0(d, blob): # QVariant::Invalid d.putBetterType("%sQVariant (invalid)" % d.qtNamespace()) diff --git a/share/qtcreator/static.pro b/share/qtcreator/static.pro index 4e0c6b8400..37a44a1aa6 100644 --- a/share/qtcreator/static.pro +++ b/share/qtcreator/static.pro @@ -72,12 +72,6 @@ for(data_dir, DATA_DIRS) { dumpinfo.input = qml/qmldump/Info.plist.in dumpinfo.output = $$IDE_DATA_PATH/qml/qmldump/Info.plist QMAKE_SUBSTITUTES += dumpinfo - puppetinfo.input = qml/qmlpuppet/qmlpuppet/Info.plist.in - puppetinfo.output = $$IDE_DATA_PATH/qml/qmlpuppet/qmlpuppet/Info.plist - QMAKE_SUBSTITUES += puppetinfo - puppet2info.input = qml/qmlpuppet/qml2puppet/Info.plist.in - puppet2info.output = $$IDE_DATA_PATH/qml/qmlpuppet/qml2puppet/Info.plist - QMAKE_SUBSTITUES += puppet2info } SRCRESOURCEDIR = $$IDE_SOURCE_TREE/src/share/qtcreator/ diff --git a/share/qtcreator/templates/wizards/projects/qmake/qtquickapplication/wizard.json b/share/qtcreator/templates/wizards/projects/qmake/qtquickapplication/wizard.json index d633eddf6b..ff84948f89 100644 --- a/share/qtcreator/templates/wizards/projects/qmake/qtquickapplication/wizard.json +++ b/share/qtcreator/templates/wizards/projects/qmake/qtquickapplication/wizard.json @@ -7,8 +7,8 @@ "trDisplayName": "Qt Quick Application", "trDisplayCategory": "Application", "icon": "qml_wizard.png", - "featuresRequired": [ "QtSupport.Wizards.FeatureQt", "QtSupport.Wizards.FeatureQtQuick.2" ], - "enabled": "${JS: [ %{Plugins} ].indexOf('QmakeProjectManager') >= 0 && [ %{Features} ].indexOf('QtSupport.Wizards.FeatureQt5.3') >= 0}", + "featuresRequired": [ "QtSupport.Wizards.FeatureQt.5.3" ], + "enabled": "%{JS: [ %{Plugins} ].indexOf('QmakeProjectManager') >= 0 }", "options": [ @@ -38,6 +38,7 @@ "type": "ComboBox", "data": { + "index": 2, "items": [ { @@ -46,8 +47,7 @@ "{ 'qtQuickVersion': '2.5', 'qtQuickWindowVersion': '2.2' - }", - "condition": "%{JS: [ %{Features} ].indexOf('QtSupport.Wizards.FeatureQt5.5') >= 0}" + }" }, { "trKey": "Qt 5.4", @@ -55,8 +55,7 @@ "{ 'qtQuickVersion': '2.4', 'qtQuickWindowVersion': '2.2' - }", - "condition": "%{JS: [ %{Features} ].indexOf('QtSupport.Wizards.FeatureQt5.4') >= 0}" + }" }, { "trKey": "Qt 5.3", @@ -64,8 +63,7 @@ "{ 'qtQuickVersion': '2.3', 'qtQuickWindowVersion': '2.2' - }", - "condition": "%{JS: [ %{Features} ].indexOf('QtSupport.Wizards.FeatureQt5.3') >= 0}" + }" } ] } diff --git a/share/qtcreator/templates/wizards/projects/qmake/qtquickcontrolsapplication/wizard.json b/share/qtcreator/templates/wizards/projects/qmake/qtquickcontrolsapplication/wizard.json index 7738f28e5a..0a57e21bf3 100644 --- a/share/qtcreator/templates/wizards/projects/qmake/qtquickcontrolsapplication/wizard.json +++ b/share/qtcreator/templates/wizards/projects/qmake/qtquickcontrolsapplication/wizard.json @@ -7,8 +7,8 @@ "trDisplayName": "Qt Quick Controls Application", "trDisplayCategory": "Application", "icon": "../qtquickapplication/qml_wizard.png", - "featuresRequired": [ "QtSupport.Wizards.FeatureQt" ], - "enabled": "%{JS: [ %{Plugins} ].indexOf('QmakeProjectManager') >= 0 && [ %{Features} ].indexOf('QtSupport.Wizards.FeatureQt5.3') >= 0 }", + "featuresRequired": [ "QtSupport.Wizards.FeatureQt.5.3" ], + "enabled": "%{JS: [ %{Plugins} ].indexOf('QmakeProjectManager') >= 0}", "options": [ @@ -40,6 +40,7 @@ "type": "ComboBox", "data": { + "index": 2, "items": [ { @@ -50,8 +51,7 @@ 'qtQuickControlsVersion': '1.4', 'qtQuickDialogsVersion': '1.2', 'qtQuickLayoutsVersion': '1.2' - }", - "condition": "%{JS: [ %{Features} ].indexOf('QtSupport.Wizards.FeatureQt5.5') >= 0}" + }" }, { "trKey": "Qt 5.4", @@ -61,8 +61,7 @@ 'qtQuickControlsVersion': '1.3', 'qtQuickDialogsVersion': '1.2', 'qtQuickLayoutsVersion': '1.1' - }", - "condition": "%{JS: [ %{Features} ].indexOf('QtSupport.Wizards.FeatureQt5.4') >= 0}" + }" }, { "trKey": "Qt 5.3", @@ -72,8 +71,7 @@ 'qtQuickControlsVersion': '1.2', 'qtQuickDialogsVersion': '1.2', 'qtQuickLayoutsVersion': '1.1' - }", - "condition": "%{JS: [ %{Features} ].indexOf('QtSupport.Wizards.FeatureQt5.3') >= 0}" + }" } ] } diff --git a/src/plugins/autotoolsprojectmanager/autotoolsproject.cpp b/src/plugins/autotoolsprojectmanager/autotoolsproject.cpp index 64053b4a5b..5af16f3e4f 100644 --- a/src/plugins/autotoolsprojectmanager/autotoolsproject.cpp +++ b/src/plugins/autotoolsprojectmanager/autotoolsproject.cpp @@ -164,8 +164,8 @@ void AutotoolsProject::loadProjectTree() // The thread is still busy parsing a previus configuration. // Wait until the thread has been finished and delete it. // TODO: Discuss whether blocking is acceptable. - disconnect(m_makefileParserThread, SIGNAL(finished()), - this, SLOT(makefileParsingFinished())); + disconnect(m_makefileParserThread, &QThread::finished, + this, &AutotoolsProject::makefileParsingFinished); m_makefileParserThread->wait(); delete m_makefileParserThread; m_makefileParserThread = 0; @@ -402,6 +402,24 @@ QList<Node *> AutotoolsProject::nodes(FolderNode *parent) const return list; } +static QStringList filterIncludes(const QString &absSrc, const QString &absBuild, + const QStringList &in) +{ + QStringList result; + foreach (const QString i, in) { + QString out = i; + out.replace(QLatin1String("$(top_srcdir)"), absSrc); + out.replace(QLatin1String("$(abs_top_srcdir)"), absSrc); + + out.replace(QLatin1String("$(top_builddir)"), absBuild); + out.replace(QLatin1String("$(abs_top_builddir)"), absBuild); + + result << out; + } + + return result; +} + void AutotoolsProject::updateCppCodeModel() { CppTools::CppModelManager *modelManager = CppTools::CppModelManager::instance(); @@ -427,7 +445,12 @@ void AutotoolsProject::updateCppCodeModel() ppBuilder.setCFlags(cflags); ppBuilder.setCxxFlags(cxxflags); - ppBuilder.setIncludePaths(m_makefileParserThread->includePaths()); + const QString absSrc = projectDirectory().toString(); + const Target *target = activeTarget(); + const QString absBuild = (target && target->activeBuildConfiguration()) + ? target->activeBuildConfiguration()->buildDirectory().toString() : QString(); + + ppBuilder.setIncludePaths(filterIncludes(absSrc, absBuild, m_makefileParserThread->includePaths())); ppBuilder.setDefines(m_makefileParserThread->defines()); const QList<Core::Id> languages = ppBuilder.createProjectPartsForFiles(m_files); diff --git a/src/plugins/autotoolsprojectmanager/makefileparser.cpp b/src/plugins/autotoolsprojectmanager/makefileparser.cpp index 3c1e94d133..48436899eb 100644 --- a/src/plugins/autotoolsprojectmanager/makefileparser.cpp +++ b/src/plugins/autotoolsprojectmanager/makefileparser.cpp @@ -33,6 +33,7 @@ #include "makefileparser.h" #include <utils/qtcassert.h> +#include <utils/qtcprocess.h> #include <QFile> #include <QDir> @@ -131,12 +132,12 @@ QByteArray MakefileParser::defines() const QStringList MakefileParser::cflags() const { - return m_cflags; + return m_cppflags + m_cflags; } QStringList MakefileParser::cxxflags() const { - return m_cxxflags; + return m_cppflags + m_cxxflags; } void MakefileParser::cancel() @@ -443,9 +444,22 @@ QString MakefileParser::parseIdentifierBeforeAssign(const QString &line) QStringList MakefileParser::parseTermsAfterAssign(const QString &line) { int assignPos = line.indexOf(QLatin1Char('=')) + 1; - if (assignPos >= line.size()) + if (assignPos <= 0 || assignPos >= line.size()) return QStringList(); - return line.mid(assignPos).split(QLatin1Char(' '), QString::SkipEmptyParts); + + const QStringList parts = Utils::QtcProcess::splitArgs(line.mid(assignPos)); + QStringList result; + for (int i = 0; i < parts.count(); ++i) { + const QString cur = parts.at(i); + const QString next = (i == parts.count() - 1) ? QString() : parts.at(i + 1); + if (cur == QLatin1String("-D") || cur == QLatin1String("-U") || cur == QLatin1String("-I")) { + result << cur + next; + ++i; + } else { + result << cur; + } + } + return result; } bool MakefileParser::maybeParseDefine(const QString &term) @@ -493,6 +507,15 @@ bool MakefileParser::maybeParseCXXFlag(const QString &term) return false; } +bool MakefileParser::maybeParseCPPFlag(const QString &term) +{ + if (term.startsWith(QLatin1Char('-'))) { + m_cppflags += term; + return true; + } + return false; +} + void MakefileParser::addAllSources() { QStringList extensions; @@ -523,6 +546,12 @@ void MakefileParser::parseIncludePaths() QString line; do { line = textStream.readLine(); + while (line.endsWith(QLatin1Char('\\'))) { + line.chop(1); + QString next = textStream.readLine(); + line.append(next); + } + const QString varName = parseIdentifierBeforeAssign(line); if (varName.isEmpty()) continue; @@ -537,11 +566,14 @@ void MakefileParser::parseIncludePaths() foreach (const QString &term, parseTermsAfterAssign(line)) maybeParseDefine(term) || maybeParseInclude(term, dirName) || maybeParseCFlag(term); - } else if (varName.endsWith(QLatin1String("CPPFLAGS")) - || varName.endsWith(QLatin1String("CXXFLAGS"))) { + } else if (varName.endsWith(QLatin1String("CXXFLAGS"))) { foreach (const QString &term, parseTermsAfterAssign(line)) maybeParseDefine(term) || maybeParseInclude(term, dirName) || maybeParseCXXFlag(term); + } else if (varName.endsWith(QLatin1String("CPPFLAGS"))) { + foreach (const QString &term, parseTermsAfterAssign(line)) + maybeParseDefine(term) || maybeParseInclude(term, dirName) + || maybeParseCPPFlag(term); } } while (!line.isNull()); diff --git a/src/plugins/autotoolsprojectmanager/makefileparser.h b/src/plugins/autotoolsprojectmanager/makefileparser.h index 463c56e65a..32569d6a11 100644 --- a/src/plugins/autotoolsprojectmanager/makefileparser.h +++ b/src/plugins/autotoolsprojectmanager/makefileparser.h @@ -258,6 +258,11 @@ private: */ bool maybeParseCXXFlag(const QString &term); + /** + * If term is compiler flag -<flag>, adds it to cppflags and returns true. + */ + bool maybeParseCPPFlag(const QString &term); + private: bool m_success; ///< Return value for MakefileParser::parse(). @@ -271,7 +276,8 @@ private: QStringList m_includePaths; ///< Return value for MakefileParser::includePaths() QByteArray m_defines; ///< Return value for MakefileParser::defines() QStringList m_cflags; ///< Return value for MakefileParser::cflags() - QStringList m_cxxflags; ///< Return value for MakefileParser::cxxflags() + QStringList m_cxxflags; ///< Return value for MakefileParser::cxxflags() + QStringList m_cppflags; ///< The cpp flags, which will be part of both cflags and cxxflags QString m_line; ///< Current line of the makefile QTextStream m_textStream; ///< Textstream that represents the makefile diff --git a/src/plugins/clangcodemodel/clangeditordocumentparser.h b/src/plugins/clangcodemodel/clangeditordocumentparser.h index cbdce90f2d..e86cf50f94 100644 --- a/src/plugins/clangcodemodel/clangeditordocumentparser.h +++ b/src/plugins/clangcodemodel/clangeditordocumentparser.h @@ -28,7 +28,6 @@ ** ****************************************************************************/ - #ifndef CLANGEDITORDOCUMENTPARSER_H #define CLANGEDITORDOCUMENTPARSER_H @@ -36,7 +35,6 @@ #include <cpptools/baseeditordocumentparser.h> - namespace CppTools { class WorkingCopy; } namespace ClangCodeModel { @@ -46,9 +44,6 @@ class ClangEditorDocumentParser : public CppTools::BaseEditorDocumentParser Q_OBJECT public: - typedef QSharedPointer<ClangEditorDocumentParser> Ptr; - -public: ClangEditorDocumentParser(const QString &filePath); void update(CppTools::WorkingCopy workingCopy) override; @@ -59,8 +54,6 @@ public: private: SemanticMarker::Ptr m_marker; - QStringList m_options; - Internal::UnsavedFiles m_unsavedFiles; }; } // namespace ClangCodeModel diff --git a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp index 36bd7aebfd..8115b52577 100644 --- a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp +++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp @@ -69,6 +69,10 @@ ModelManagerSupportClang::ModelManagerSupportClang() this, &ModelManagerSupportClang::onEditorOpened); CppTools::CppModelManager *modelManager = cppModelManager(); + connect(modelManager, &CppTools::CppModelManager::abstractEditorSupportContentsUpdated, + this, &ModelManagerSupportClang::onAbstractEditorSupportContentsUpdated); + connect(modelManager, &CppTools::CppModelManager::abstractEditorSupportRemoved, + this, &ModelManagerSupportClang::onAbstractEditorSupportRemoved); connect(modelManager, &CppTools::CppModelManager::projectPartsUpdated, this, &ModelManagerSupportClang::onProjectPartsUpdated); connect(modelManager, &CppTools::CppModelManager::projectPartsRemoved, @@ -113,9 +117,8 @@ void ModelManagerSupportClang::onEditorOpened(Core::IEditor *editor) Core::IDocument *document = editor->document(); QTC_ASSERT(document, return); TextEditor::TextDocument *textDocument = qobject_cast<TextEditor::TextDocument *>(document); - QTC_ASSERT(textDocument, return); - if (cppModelManager()->isCppEditor(editor)) { + if (textDocument && cppModelManager()->isCppEditor(editor)) { // Handle externally changed documents connect(textDocument, &Core::IDocument::reloadFinished, this, &ModelManagerSupportClang::onCppDocumentReloadFinished, diff --git a/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp b/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp index 58de825850..95c43eeb20 100644 --- a/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp +++ b/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp @@ -615,6 +615,102 @@ bool hasSnippet(ProposalModel model, const QByteArray &text) return false; } +class MonitorGeneratedUiFile : public QObject +{ + Q_OBJECT + +public: + MonitorGeneratedUiFile(); + bool waitUntilGenerated(int timeout = 10000) const; + +private: + void onUiFileGenerated() { m_isGenerated = true; } + + bool m_isGenerated = false; +}; + +MonitorGeneratedUiFile::MonitorGeneratedUiFile() +{ + connect(CppTools::CppModelManager::instance(), + &CppTools::CppModelManager::abstractEditorSupportContentsUpdated, + this, &MonitorGeneratedUiFile::onUiFileGenerated); +} + +bool MonitorGeneratedUiFile::waitUntilGenerated(int timeout) const +{ + if (m_isGenerated) + return true; + + QTime time; + time.start(); + + forever { + if (m_isGenerated) + return true; + + if (time.elapsed() > timeout) + return false; + + QCoreApplication::processEvents(); + QThread::msleep(20); + } + + return false; +} + +class WriteFileAndWaitForReloadedDocument : public QObject +{ +public: + WriteFileAndWaitForReloadedDocument(const QString &filePath, + const QByteArray &fileContents, + Core::IDocument *document) + : m_filePath(filePath) + , m_fileContents(fileContents) + { + QTC_CHECK(document); + connect(document, &Core::IDocument::reloadFinished, + this, &WriteFileAndWaitForReloadedDocument::onReloadFinished); + } + + void onReloadFinished() + { + m_onReloadFinished = true; + } + + bool wait() const + { + QTC_ASSERT(writeFile(m_filePath, m_fileContents), return false); + + QTime totalTime; + totalTime.start(); + + QTime writeFileAgainTime; + writeFileAgainTime.start(); + + forever { + if (m_onReloadFinished) + return true; + + if (totalTime.elapsed() > 10000) + return false; + + if (writeFileAgainTime.elapsed() > 1000) { + // The timestamp did not change, try again now. + QTC_ASSERT(writeFile(m_filePath, m_fileContents), return false); + writeFileAgainTime.restart(); + } + + QCoreApplication::processEvents(); + QThread::msleep(20); + } + } + +private: + bool m_onReloadFinished = false; + QString m_filePath; + QByteArray m_fileContents; +}; + } // anonymous namespace namespace ClangCodeModel { @@ -866,11 +962,11 @@ void ClangCodeCompletionTest::testUnsavedFilesTrackingByModifyingIncludedFileExt ProposalModel proposal = completionResults(openSource.editor()); QVERIFY(hasItem(proposal, "globalFromHeader")); - // Simulate external modification - QThread::sleep(1); // Ensures different time stamp and thus that the difference will be noticed - QVERIFY(writeFile(headerDocument.filePath, "int globalFromHeaderReloaded;\n")); - QSignalSpy waitForReloadedDocument(openHeader.editor()->document(), - SIGNAL(reloadFinished(bool))); + // Simulate external modification and wait for reload + WriteFileAndWaitForReloadedDocument waitForReloadedDocument( + headerDocument.filePath, + "int globalFromHeaderReloaded;\n", + openHeader.editor()->document()); QVERIFY(waitForReloadedDocument.wait()); // Retrigger completion and check if its updated @@ -883,6 +979,8 @@ void ClangCodeCompletionTest::testUnsavedFilesTrackingByCompletingUiObject() CppTools::Tests::TemporaryCopiedDir testDir(qrcPath("qt-widgets-app")); QVERIFY(testDir.isValid()); + MonitorGeneratedUiFile monitorGeneratedUiFile; + // Open project const QString projectFilePath = testDir.absolutePath("qt-widgets-app.pro"); CppTools::Tests::ProjectOpenerAndCloser projectManager; @@ -897,11 +995,11 @@ void ClangCodeCompletionTest::testUnsavedFilesTrackingByCompletingUiObject() QVERIFY(openSource.succeeded()); // ...and check comletions + QVERIFY(monitorGeneratedUiFile.waitUntilGenerated()); ProposalModel proposal = completionResults(openSource.editor()); QVERIFY(hasItem(proposal, "menuBar")); QVERIFY(hasItem(proposal, "statusBar")); QVERIFY(hasItem(proposal, "centralWidget")); - QEXPECT_FAIL("", "Signals are not yet done", Abort); QVERIFY(hasItem(proposal, "setupUi")); } @@ -921,6 +1019,7 @@ void ClangCodeCompletionTest::testUpdateBackendAfterRestart() // ... and modify it, so we have an unsaved file. insertTextAtTopOfEditor(openHeader.editor(), "int someGlobal;\n"); // Open project ... + MonitorGeneratedUiFile monitorGeneratedUiFile; const QString projectFilePath = testDir.absolutePath("qt-widgets-app.pro"); CppTools::Tests::ProjectOpenerAndCloser projectManager; const CppTools::ProjectInfo projectInfo = projectManager.open(projectFilePath, true); @@ -931,6 +1030,7 @@ void ClangCodeCompletionTest::testUpdateBackendAfterRestart() QVERIFY(testDocument.isCreatedAndHasValidCursorPosition()); OpenEditorAtCursorPosition openSource(testDocument); QVERIFY(openSource.succeeded()); + QVERIFY(monitorGeneratedUiFile.waitUntilGenerated()); // Check commands that would have been sent QVERIFY(compare(LogOutput(spy.senderLog), @@ -939,6 +1039,8 @@ void ClangCodeCompletionTest::testUpdateBackendAfterRestart() " ProjectPartContainer id: qt-widgets-app.pro\n" "RegisterTranslationUnitForCodeCompletionCommand\n" " Path: myheader.h ProjectPart: \n" + "RegisterTranslationUnitForCodeCompletionCommand\n" + " Path: ui_mainwindow.h ProjectPart: \n" ))); spy.senderLog.clear(); @@ -966,3 +1068,5 @@ void ClangCodeCompletionTest::testUpdateBackendAfterRestart() } // namespace Tests } // namespace Internal } // namespace ClangCodeModel + +#include "clangcodecompletion_test.moc" diff --git a/src/plugins/cpptools/abstracteditorsupport.cpp b/src/plugins/cpptools/abstracteditorsupport.cpp index e9f77f9e31..843ce0f06f 100644 --- a/src/plugins/cpptools/abstracteditorsupport.cpp +++ b/src/plugins/cpptools/abstracteditorsupport.cpp @@ -53,6 +53,11 @@ void AbstractEditorSupport::updateDocument() m_modelmanager->updateSourceFiles(QSet<QString>() << fileName()); } +void AbstractEditorSupport::notifyAboutUpdatedContents() const +{ + m_modelmanager->emitAbstractEditorSupportContentsUpdated(fileName(), contents()); +} + QString AbstractEditorSupport::licenseTemplate(const QString &file, const QString &className) { return Internal::CppFileSettings::licenseTemplate(file, className); diff --git a/src/plugins/cpptools/abstracteditorsupport.h b/src/plugins/cpptools/abstracteditorsupport.h index b1919fcff2..606fcca9a2 100644 --- a/src/plugins/cpptools/abstracteditorsupport.h +++ b/src/plugins/cpptools/abstracteditorsupport.h @@ -51,6 +51,7 @@ public: virtual QString fileName() const = 0; void updateDocument(); + void notifyAboutUpdatedContents() const; unsigned revision() const { return m_revision; } static QString licenseTemplate(const QString &file = QString(), const QString &className = QString()); diff --git a/src/plugins/cpptools/baseeditordocumentparser.h b/src/plugins/cpptools/baseeditordocumentparser.h index 544bc92e47..ddd12fbb0a 100644 --- a/src/plugins/cpptools/baseeditordocumentparser.h +++ b/src/plugins/cpptools/baseeditordocumentparser.h @@ -41,8 +41,6 @@ namespace CppTools { class CPPTOOLS_EXPORT BaseEditorDocumentParser : public QObject { Q_OBJECT - Q_DISABLE_COPY(BaseEditorDocumentParser) - BaseEditorDocumentParser(); public: BaseEditorDocumentParser(const QString &filePath); diff --git a/src/plugins/cpptools/baseeditordocumentprocessor.h b/src/plugins/cpptools/baseeditordocumentprocessor.h index 8054d866e5..4f30a519e3 100644 --- a/src/plugins/cpptools/baseeditordocumentprocessor.h +++ b/src/plugins/cpptools/baseeditordocumentprocessor.h @@ -49,8 +49,6 @@ namespace CppTools { class CPPTOOLS_EXPORT BaseEditorDocumentProcessor : public QObject { Q_OBJECT - Q_DISABLE_COPY(BaseEditorDocumentProcessor) - BaseEditorDocumentProcessor(); public: BaseEditorDocumentProcessor(TextEditor::TextDocument *document); diff --git a/src/plugins/cpptools/builtineditordocumentprocessor.h b/src/plugins/cpptools/builtineditordocumentprocessor.h index 396ba1bf1a..b0db31e009 100644 --- a/src/plugins/cpptools/builtineditordocumentprocessor.h +++ b/src/plugins/cpptools/builtineditordocumentprocessor.h @@ -42,7 +42,6 @@ namespace CppTools { class CPPTOOLS_EXPORT BuiltinEditorDocumentProcessor : public BaseEditorDocumentProcessor { Q_OBJECT - BuiltinEditorDocumentProcessor(); public: BuiltinEditorDocumentProcessor(TextEditor::TextDocument *document, diff --git a/src/plugins/cpptools/cppeditoroutline.h b/src/plugins/cpptools/cppeditoroutline.h index c40ef4327d..7017b44d9b 100644 --- a/src/plugins/cpptools/cppeditoroutline.h +++ b/src/plugins/cpptools/cppeditoroutline.h @@ -51,7 +51,6 @@ namespace CppTools { class CPPTOOLS_EXPORT CppEditorOutline : public QObject { Q_OBJECT - Q_DISABLE_COPY(CppEditorOutline) public: explicit CppEditorOutline(TextEditor::TextEditorWidget *editorWidget); diff --git a/src/plugins/cpptools/cppmodelmanager.cpp b/src/plugins/cpptools/cppmodelmanager.cpp index 01132b9ebb..0d1b13621d 100644 --- a/src/plugins/cpptools/cppmodelmanager.cpp +++ b/src/plugins/cpptools/cppmodelmanager.cpp @@ -958,6 +958,17 @@ void CppModelManager::emitDocumentUpdated(Document::Ptr doc) emit documentUpdated(doc); } +void CppModelManager::emitAbstractEditorSupportContentsUpdated(const QString &filePath, + const QByteArray &contents) +{ + emit abstractEditorSupportContentsUpdated(filePath, contents); +} + +void CppModelManager::emitAbstractEditorSupportRemoved(const QString &filePath) +{ + emit abstractEditorSupportRemoved(filePath); +} + void CppModelManager::onProjectAdded(ProjectExplorer::Project *) { QMutexLocker locker(&d->m_projectMutex); diff --git a/src/plugins/cpptools/cppmodelmanager.h b/src/plugins/cpptools/cppmodelmanager.h index 9a141ac8e3..24b2d2d6ee 100644 --- a/src/plugins/cpptools/cppmodelmanager.h +++ b/src/plugins/cpptools/cppmodelmanager.h @@ -111,6 +111,9 @@ public: bool replaceDocument(Document::Ptr newDoc); void emitDocumentUpdated(CPlusPlus::Document::Ptr doc); + void emitAbstractEditorSupportContentsUpdated(const QString &filePath, + const QByteArray &contents); + void emitAbstractEditorSupportRemoved(const QString &filePath); bool isCppEditor(Core::IEditor *editor) const; @@ -173,6 +176,9 @@ signals: void gcFinished(); // Needed for tests. + void abstractEditorSupportContentsUpdated(const QString &filePath, const QByteArray &contents); + void abstractEditorSupportRemoved(const QString &filePath); + public slots: void updateModifiedSourceFiles(); void GC(); diff --git a/src/plugins/cpptools/cppsemanticinfoupdater.h b/src/plugins/cpptools/cppsemanticinfoupdater.h index 40eb7a2749..9b36254326 100644 --- a/src/plugins/cpptools/cppsemanticinfoupdater.h +++ b/src/plugins/cpptools/cppsemanticinfoupdater.h @@ -43,7 +43,6 @@ class SemanticInfoUpdaterPrivate; class SemanticInfoUpdater : public QObject { Q_OBJECT - Q_DISABLE_COPY(SemanticInfoUpdater) public: explicit SemanticInfoUpdater(); diff --git a/src/plugins/cpptools/semantichighlighter.h b/src/plugins/cpptools/semantichighlighter.h index d00db6e9da..8485539e2e 100644 --- a/src/plugins/cpptools/semantichighlighter.h +++ b/src/plugins/cpptools/semantichighlighter.h @@ -49,7 +49,6 @@ namespace CppTools { class CPPTOOLS_EXPORT SemanticHighlighter : public QObject { Q_OBJECT - Q_DISABLE_COPY(SemanticHighlighter) public: enum Kind { diff --git a/src/plugins/debugger/debugger.qbs b/src/plugins/debugger/debugger.qbs index 3322d95b83..f682cdc4b5 100644 --- a/src/plugins/debugger/debugger.qbs +++ b/src/plugins/debugger/debugger.qbs @@ -145,16 +145,13 @@ QtcPlugin { name: "QML Debugger" prefix: "qml/" files: [ - "baseqmldebuggerclient.cpp", "baseqmldebuggerclient.h", "interactiveinterpreter.cpp", "interactiveinterpreter.h", - "qmladapter.cpp", "qmladapter.h", "qmlcppengine.cpp", "qmlcppengine.h", "qmlengine.cpp", "qmlengine.h", + "qmlengineutils.cpp", "qmlengineutils.h", "qmlinspectoradapter.cpp", "qmlinspectoradapter.h", "qmlinspectoragent.cpp", "qmlinspectoragent.h", - "qmlv8debuggerclient.cpp", "qmlv8debuggerclient.h", - "qmlv8debuggerclientconstants.h", - "qscriptdebuggerclient.cpp", "qscriptdebuggerclient.h" + "qmlv8debuggerclientconstants.h" ] } diff --git a/src/plugins/debugger/qml/baseqmldebuggerclient.cpp b/src/plugins/debugger/qml/baseqmldebuggerclient.cpp deleted file mode 100644 index 5a7f9287a1..0000000000 --- a/src/plugins/debugger/qml/baseqmldebuggerclient.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/**************************************************************************** -** -** 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 "baseqmldebuggerclient.h" -#include <debugger/breakhandler.h> - -#include <utils/qtcassert.h> - -namespace Debugger { -namespace Internal { - -class BaseQmlDebuggerClientPrivate -{ -public: - QList<QByteArray> sendBuffer; -}; - -BaseQmlDebuggerClient::BaseQmlDebuggerClient(QmlDebug::QmlDebugConnection* client, QLatin1String clientName) - : QmlDebugClient(clientName, client), - d(new BaseQmlDebuggerClientPrivate()) -{ -} - -BaseQmlDebuggerClient::~BaseQmlDebuggerClient() -{ - delete d; -} - -bool BaseQmlDebuggerClient::acceptsBreakpoint(Breakpoint /*bp*/) -{ - return false; -} - -void BaseQmlDebuggerClient::stateChanged(State state) -{ - emit newState(state); -} - -void BaseQmlDebuggerClient::sendMessage(const QByteArray &msg) -{ - if (state() == Enabled) - QmlDebugClient::sendMessage(msg); - else - d->sendBuffer.append(msg); -} - -void BaseQmlDebuggerClient::flushSendBuffer() -{ - QTC_ASSERT(state() == Enabled, return); - foreach (const QByteArray &msg, d->sendBuffer) - QmlDebugClient::sendMessage(msg); - d->sendBuffer.clear(); -} - -} // Internal -} // Debugger diff --git a/src/plugins/debugger/qml/baseqmldebuggerclient.h b/src/plugins/debugger/qml/baseqmldebuggerclient.h deleted file mode 100644 index 47435fbf99..0000000000 --- a/src/plugins/debugger/qml/baseqmldebuggerclient.h +++ /dev/null @@ -1,111 +0,0 @@ -/**************************************************************************** -** -** 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 BASEQMLDEBUGGERCLIENT_H -#define BASEQMLDEBUGGERCLIENT_H - -#include <debugger/debuggerengine.h> -#include <qmldebug/qmldebugclient.h> - -namespace Debugger { -namespace Internal { - -class WatchData; -class WatchItem; -class BreakHandler; -class BreakpointModelId; -class QmlEngine; -class BaseQmlDebuggerClientPrivate; - -class BaseQmlDebuggerClient : public QmlDebug::QmlDebugClient -{ - Q_OBJECT - -public: - BaseQmlDebuggerClient(QmlDebug::QmlDebugConnection* client, QLatin1String clientName); - virtual ~BaseQmlDebuggerClient(); - - virtual void startSession() = 0; - virtual void endSession() = 0; - virtual void resetSession() = 0; - - virtual void executeStep() = 0; - virtual void executeStepOut() = 0; - virtual void executeNext() = 0; - virtual void executeStepI() = 0; - - virtual void executeRunToLine(const ContextData &data) = 0; - - virtual void continueInferior() = 0; - virtual void interruptInferior() = 0; - - virtual void activateFrame(int index) = 0; - - virtual bool acceptsBreakpoint(Breakpoint bp); - virtual void insertBreakpoint(Breakpoint bp, int adjustedLine, - int adjustedColumn = -1) = 0; - virtual void removeBreakpoint(Breakpoint bp) = 0; - virtual void changeBreakpoint(Breakpoint bp) = 0; - virtual void synchronizeBreakpoints() = 0; - - virtual void assignValueInDebugger(const WatchData *data, - const QString &expression, - const QVariant &valueV) = 0; - - virtual void updateWatchData(const WatchData &data) = 0; - virtual void executeDebuggerCommand(const QString &command) = 0; - - virtual void synchronizeWatchers(const QStringList &watchers) = 0; - - virtual void expandObject(const QByteArray &iname, quint64 objectId) = 0; - - virtual void setEngine(QmlEngine *engine) = 0; - - virtual void getSourceFiles() {} - - void flushSendBuffer(); - -signals: - void newState(QmlDebug::QmlDebugClient::State state); - void stackFrameCompleted(); - -protected: - virtual void stateChanged(State state); - void sendMessage(const QByteArray &msg); - -private: - BaseQmlDebuggerClientPrivate *d; - friend class BaseQmlDebuggerClientPrivate; -}; - -} // Internal -} // Debugger - -#endif // BASEQMLDEBUGGERCLIENT_H diff --git a/src/plugins/debugger/qml/qml.pri b/src/plugins/debugger/qml/qml.pri index 3dd714070d..18ed7fe1f5 100644 --- a/src/plugins/debugger/qml/qml.pri +++ b/src/plugins/debugger/qml/qml.pri @@ -1,10 +1,7 @@ HEADERS += \ $$PWD/qmlengine.h \ - $$PWD/qmladapter.h \ - $$PWD/baseqmldebuggerclient.h \ + $$PWD/qmlengineutils.h \ $$PWD/qmlcppengine.h \ - $$PWD/qscriptdebuggerclient.h \ - $$PWD/qmlv8debuggerclient.h \ $$PWD/interactiveinterpreter.h \ $$PWD/qmlv8debuggerclientconstants.h \ $$PWD/qmlinspectoragent.h \ @@ -12,11 +9,8 @@ HEADERS += \ SOURCES += \ $$PWD/qmlengine.cpp \ - $$PWD/qmladapter.cpp \ - $$PWD/baseqmldebuggerclient.cpp \ + $$PWD/qmlengineutils.cpp \ $$PWD/qmlcppengine.cpp \ - $$PWD/qscriptdebuggerclient.cpp \ - $$PWD/qmlv8debuggerclient.cpp \ $$PWD/interactiveinterpreter.cpp \ $$PWD/qmlinspectoragent.cpp \ $$PWD/qmlinspectoradapter.cpp diff --git a/src/plugins/debugger/qml/qmladapter.cpp b/src/plugins/debugger/qml/qmladapter.cpp deleted file mode 100644 index 73f29d0400..0000000000 --- a/src/plugins/debugger/qml/qmladapter.cpp +++ /dev/null @@ -1,242 +0,0 @@ -/**************************************************************************** -** -** 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 "qmladapter.h" - -#include <debugger/debuggerstringutils.h> -#include "qmlengine.h" -#include "qmlv8debuggerclient.h" -#include "qscriptdebuggerclient.h" - -#include <utils/qtcassert.h> - -#include <QDebug> - -using namespace QmlDebug; - -namespace Debugger { -namespace Internal { - -/*! - QmlAdapter manages the connection & clients for QML/JS debugging. - */ - -QmlAdapter::QmlAdapter(DebuggerEngine *engine, QObject *parent) - : QObject(parent) - , m_engine(engine) - , m_qmlClient(0) - , m_conn(0) - , m_msgClient(0) -{ - m_connectionTimer.setInterval(4000); - m_connectionTimer.setSingleShot(true); - connect(&m_connectionTimer, &QTimer::timeout, this, &QmlAdapter::checkConnectionState); - - m_conn = new QmlDebugConnection(this); - connect(m_conn, &QmlDebugConnection::stateMessage, - this, &QmlAdapter::showConnectionStateMessage); - connect(m_conn, &QmlDebugConnection::errorMessage, - this, &QmlAdapter::showConnectionErrorMessage); - connect(m_conn, &QmlDebugConnection::error, - this, &QmlAdapter::connectionErrorOccurred); - connect(m_conn, &QmlDebugConnection::opened, - &m_connectionTimer, &QTimer::stop); - connect(m_conn, &QmlDebugConnection::opened, - this, &QmlAdapter::connected); - connect(m_conn, &QmlDebugConnection::closed, - this, &QmlAdapter::disconnected); - - createDebuggerClients(); - m_msgClient = new QDebugMessageClient(m_conn); - connect(m_msgClient, &QDebugMessageClient::newState, this, &QmlAdapter::clientStateChanged); - -} - -QmlAdapter::~QmlAdapter() -{ -} - -void QmlAdapter::beginConnectionTcp(const QString &address, quint16 port) -{ - if (m_engine.isNull() || !m_conn || m_conn->isOpen()) - return; - - m_conn->connectToHost(address, port); - - //A timeout to check the connection state - m_connectionTimer.start(); -} - -void QmlAdapter::closeConnection() -{ - if (m_connectionTimer.isActive()) { - m_connectionTimer.stop(); - } else { - if (m_conn) - m_conn->close(); - } -} - -void QmlAdapter::connectionErrorOccurred(QDebugSupport::Error error) -{ - // this is only an error if we are already connected and something goes wrong. - if (isConnected()) { - emit connectionError(error); - } else { - m_connectionTimer.stop(); - emit connectionStartupFailed(); - } -} - -void QmlAdapter::clientStateChanged(QmlDebugClient::State state) -{ - QString serviceName; - float version = 0; - if (QmlDebugClient *client = qobject_cast<QmlDebugClient*>(sender())) { - serviceName = client->name(); - version = client->remoteVersion(); - } - - logServiceStateChange(serviceName, version, state); -} - -void QmlAdapter::debugClientStateChanged(QmlDebugClient::State state) -{ - if (state != QmlDebugClient::Enabled) - return; - QmlDebugClient *client = qobject_cast<QmlDebugClient*>(sender()); - QTC_ASSERT(client, return); - - m_qmlClient = qobject_cast<BaseQmlDebuggerClient *>(client); - m_qmlClient->startSession(); -} - -void QmlAdapter::checkConnectionState() -{ - if (!isConnected()) { - closeConnection(); - emit connectionStartupFailed(); - } -} - -bool QmlAdapter::isConnected() const -{ - return m_conn && m_qmlClient && m_conn->isOpen(); -} - -void QmlAdapter::createDebuggerClients() -{ - QScriptDebuggerClient *debugClient1 = new QScriptDebuggerClient(m_conn); - connect(debugClient1, &QScriptDebuggerClient::newState, - this, &QmlAdapter::clientStateChanged); - connect(debugClient1, &QScriptDebuggerClient::newState, - this, &QmlAdapter::debugClientStateChanged); - - QmlV8DebuggerClient *debugClient2 = new QmlV8DebuggerClient(m_conn); - connect(debugClient2, &QmlV8DebuggerClient::newState, - this, &QmlAdapter::clientStateChanged); - connect(debugClient2, &QmlV8DebuggerClient::newState, - this, &QmlAdapter::debugClientStateChanged); - - m_debugClients.insert(debugClient1->name(),debugClient1); - m_debugClients.insert(debugClient2->name(),debugClient2); - - debugClient1->setEngine((QmlEngine*)(m_engine.data())); - debugClient2->setEngine((QmlEngine*)(m_engine.data())); -} - -QmlDebugConnection *QmlAdapter::connection() const -{ - return m_conn; -} - -DebuggerEngine *QmlAdapter::debuggerEngine() const -{ - return m_engine.data(); -} - -void QmlAdapter::showConnectionStateMessage(const QString &message) -{ - if (!m_engine.isNull()) - m_engine.data()->showMessage(_("QML Debugger: ") + message, LogStatus); -} - -void QmlAdapter::showConnectionErrorMessage(const QString &message) -{ - if (!m_engine.isNull()) - m_engine.data()->showMessage(_("QML Debugger: ") + message, LogError); -} - -BaseQmlDebuggerClient *QmlAdapter::activeDebuggerClient() const -{ - return m_qmlClient; -} - -QHash<QString, BaseQmlDebuggerClient*> QmlAdapter::debuggerClients() const -{ - return m_debugClients; -} - -QDebugMessageClient *QmlAdapter::messageClient() const -{ - return m_msgClient; -} - -void QmlAdapter::logServiceStateChange(const QString &service, float version, - QmlDebugClient::State newState) -{ - switch (newState) { - case QmlDebugClient::Unavailable: { - showConnectionStateMessage(_("Status of \"%1\" Version: %2 changed to 'unavailable'."). - arg(service).arg(QString::number(version))); - break; - } - case QmlDebugClient::Enabled: { - showConnectionStateMessage(_("Status of \"%1\" Version: %2 changed to 'enabled'."). - arg(service).arg(QString::number(version))); - break; - } - - case QmlDebugClient::NotConnected: { - showConnectionStateMessage(_("Status of \"%1\" Version: %2 changed to 'not connected'."). - arg(service).arg(QString::number(version))); - break; - } - } -} - -void QmlAdapter::logServiceActivity(const QString &service, const QString &logMessage) -{ - if (!m_engine.isNull()) - m_engine.data()->showMessage(service + QLatin1Char(' ') + logMessage, LogDebug); -} - -} // namespace Internal -} // namespace Debugger diff --git a/src/plugins/debugger/qml/qmladapter.h b/src/plugins/debugger/qml/qmladapter.h deleted file mode 100644 index e9f3936878..0000000000 --- a/src/plugins/debugger/qml/qmladapter.h +++ /dev/null @@ -1,107 +0,0 @@ -/**************************************************************************** -** -** 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 QMLADAPTER_H -#define QMLADAPTER_H - -#include <qmldebug/qmldebugclient.h> - -#include <QPointer> -#include <QTimer> - -namespace QmlDebug { -class BaseEngineDebugClient; -class QmlDebugConnection; -class QDebugMessageClient; -} - -namespace Debugger { -namespace Internal { - -class BaseQmlDebuggerClient; -class DebuggerEngine; -class QmlAdapterPrivate; - -class QmlAdapter : public QObject -{ - Q_OBJECT - -public: - explicit QmlAdapter(DebuggerEngine *engine, QObject *parent = 0); - virtual ~QmlAdapter(); - - void beginConnectionTcp(const QString &address, quint16 port); - void closeConnection(); - - QmlDebug::QmlDebugConnection *connection() const; - DebuggerEngine *debuggerEngine() const; - - BaseQmlDebuggerClient *activeDebuggerClient() const; - QHash<QString, BaseQmlDebuggerClient*> debuggerClients() const; - - QmlDebug::QDebugMessageClient *messageClient() const; - -public slots: - void logServiceStateChange(const QString &service, float version, - QmlDebug::QmlDebugClient::State newState); - void logServiceActivity(const QString &service, const QString &logMessage); - -signals: - void connected(); - void disconnected(); - void connectionStartupFailed(); - void connectionError(QDebugSupport::Error error); - void serviceConnectionError(const QString serviceName); - -private slots: - void connectionErrorOccurred(QDebugSupport::Error socketError); - void clientStateChanged(QmlDebug::QmlDebugClient::State state); - void debugClientStateChanged(QmlDebug::QmlDebugClient::State state); - void checkConnectionState(); - void showConnectionStateMessage(const QString &message); - void showConnectionErrorMessage(const QString &message); - -private: - bool isConnected() const; - void createDebuggerClients(); - -private: - QPointer<DebuggerEngine> m_engine; - BaseQmlDebuggerClient *m_qmlClient; - QTimer m_connectionTimer; - QmlDebug::QmlDebugConnection *m_conn; - QHash<QString, BaseQmlDebuggerClient*> m_debugClients; - QmlDebug::QDebugMessageClient *m_msgClient; -}; - -} // namespace Internal -} // namespace Debugger - -#endif // QMLADAPTER_H diff --git a/src/plugins/debugger/qml/qmlengine.cpp b/src/plugins/debugger/qml/qmlengine.cpp index c2b2ca3246..46e2796446 100644 --- a/src/plugins/debugger/qml/qmlengine.cpp +++ b/src/plugins/debugger/qml/qmlengine.cpp @@ -29,46 +29,53 @@ ****************************************************************************/ #include "qmlengine.h" -#include "baseqmldebuggerclient.h" + +#include "interactiveinterpreter.h" +#include "qmlinspectoradapter.h" #include "qmlinspectoragent.h" +#include "qmlv8debuggerclientconstants.h" +#include "qmlengineutils.h" +#include <debugger/breakhandler.h> #include <debugger/debuggeractions.h> #include <debugger/debuggercore.h> #include <debugger/debuggerinternalconstants.h> -#include <debugger/debuggermainwindow.h> #include <debugger/debuggerruncontrol.h> -#include <debugger/debuggerstartparameters.h> #include <debugger/debuggerstringutils.h> #include <debugger/debuggertooltipmanager.h> -#include <debugger/localsandexpressionswindow.h> +#include <debugger/sourcefileshandler.h> +#include <debugger/stackhandler.h> #include <debugger/threaddata.h> +#include <debugger/watchhandler.h> #include <debugger/watchwindow.h> -#include <debugger/breakhandler.h> -#include <debugger/stackhandler.h> -#include <debugger/watchhandler.h> -#include <debugger/sourcefileshandler.h> +#include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/helpmanager.h> +#include <coreplugin/icore.h> + +#include <projectexplorer/applicationlauncher.h> #include <qmljseditor/qmljseditorconstants.h> -#include <qmljs/parser/qmljsast_p.h> #include <qmljs/qmljsmodelmanagerinterface.h> #include <qmljs/consolemanagerinterface.h> -#include <utils/qtcassert.h> - #include <texteditor/textdocument.h> #include <texteditor/texteditor.h> -#include <coreplugin/editormanager/editormanager.h> -#include <coreplugin/helpmanager.h> -#include <coreplugin/icore.h> + +#include <utils/qtcassert.h> #include <QDebug> #include <QDir> #include <QDockWidget> +#include <QFileInfo> +#include <QJsonArray> +#include <QJsonDocument> +#include <QJsonObject> #include <QMessageBox> #include <QPlainTextEdit> +#include <QTimer> -#define DEBUG_QML 1 +#define DEBUG_QML 0 #if DEBUG_QML # define SDEBUG(s) qDebug() << s #else @@ -76,185 +83,139 @@ #endif # define XSDEBUG(s) qDebug() << s +using namespace Core; +using namespace ProjectExplorer; +using namespace QmlDebug; using namespace QmlJS; -using namespace AST; +using namespace TextEditor; namespace Debugger { namespace Internal { -static QTreeView *inspectorTreeView() +enum Exceptions { - return Internal::inspectorView(); -} + NoExceptions, + UncaughtExceptions, + AllExceptions +}; -class ASTWalker : public Visitor +enum StepAction { -public: - void operator()(Node *ast, quint32 *l, quint32 *c) - { - done = false; - line = l; - column = c; - Node::accept(ast, this); - } - - bool preVisit(Node *ast) - { - return ast->lastSourceLocation().startLine >= *line && !done; - } - - //Case 1: Breakpoint is between sourceStart(exclusive) and - // sourceEnd(inclusive) --> End tree walk. - //Case 2: Breakpoint is on sourceStart --> Check for the start - // of the first executable code. Set the line number and - // column number. End tree walk. - //Case 3: Breakpoint is on "unbreakable" code --> Find the next "breakable" - // code and check for Case 2. End tree walk. - - //Add more types when suitable. - - bool visit(UiScriptBinding *ast) - { - if (!ast->statement) - return true; - - quint32 sourceStartLine = ast->firstSourceLocation().startLine; - quint32 statementStartLine; - quint32 statementColumn; - - if (ast->statement->kind == Node::Kind_ExpressionStatement) { - statementStartLine = ast->statement->firstSourceLocation(). - startLine; - statementColumn = ast->statement->firstSourceLocation().startColumn; - - } else if (ast->statement->kind == Node::Kind_Block) { - Block *block = static_cast<Block *>(ast->statement); - if (!block || !block->statements) - return true; - statementStartLine = block->statements->firstSourceLocation(). - startLine; - statementColumn = block->statements->firstSourceLocation(). - startColumn; - - } else { - return true; - } - - - //Case 1 - //Check for possible relocation within the binding statement - - //Rewritten to (function <token>() { { }}) - //The offset 16 is position of inner lbrace without token length. - const int offset = 16; - - //Case 2 - if (statementStartLine == *line) { - if (sourceStartLine == *line) - *column = offset + ast->qualifiedId->identifierToken.length; - done = true; - } - - //Case 3 - if (statementStartLine > *line) { - *line = statementStartLine; - if (sourceStartLine == *line) - *column = offset + ast->qualifiedId->identifierToken.length; - else - *column = statementColumn; - done = true; - } - return true; - } - - bool visit(FunctionDeclaration *ast) { - quint32 sourceStartLine = ast->firstSourceLocation().startLine; - quint32 sourceStartColumn = ast->firstSourceLocation().startColumn; - quint32 statementStartLine = ast->body->firstSourceLocation().startLine; - quint32 statementColumn = ast->body->firstSourceLocation().startColumn; - - //Case 1 - //Check for possible relocation within the function declaration - - //Case 2 - if (statementStartLine == *line) { - if (sourceStartLine == *line) - *column = statementColumn - sourceStartColumn + 1; - done = true; - } + Continue, + StepIn, + StepOut, + Next +}; - //Case 3 - if (statementStartLine > *line) { - *line = statementStartLine; - if (sourceStartLine == *line) - *column = statementColumn - sourceStartColumn + 1; - else - *column = statementColumn; - done = true; - } - return true; - } +struct QmlV8ObjectData +{ + int handle; + QByteArray name; + QByteArray type; + QVariant value; + QVariantList properties; +}; - bool visit(EmptyStatement *ast) - { - *line = ast->lastSourceLocation().startLine + 1; - return true; - } - - bool visit(VariableStatement *ast) { test(ast); return true; } - bool visit(VariableDeclarationList *ast) { test(ast); return true; } - bool visit(VariableDeclaration *ast) { test(ast); return true; } - bool visit(ExpressionStatement *ast) { test(ast); return true; } - bool visit(IfStatement *ast) { test(ast); return true; } - bool visit(DoWhileStatement *ast) { test(ast); return true; } - bool visit(WhileStatement *ast) { test(ast); return true; } - bool visit(ForStatement *ast) { test(ast); return true; } - bool visit(LocalForStatement *ast) { test(ast); return true; } - bool visit(ForEachStatement *ast) { test(ast); return true; } - bool visit(LocalForEachStatement *ast) { test(ast); return true; } - bool visit(ContinueStatement *ast) { test(ast); return true; } - bool visit(BreakStatement *ast) { test(ast); return true; } - bool visit(ReturnStatement *ast) { test(ast); return true; } - bool visit(WithStatement *ast) { test(ast); return true; } - bool visit(SwitchStatement *ast) { test(ast); return true; } - bool visit(CaseBlock *ast) { test(ast); return true; } - bool visit(CaseClauses *ast) { test(ast); return true; } - bool visit(CaseClause *ast) { test(ast); return true; } - bool visit(DefaultClause *ast) { test(ast); return true; } - bool visit(LabelledStatement *ast) { test(ast); return true; } - bool visit(ThrowStatement *ast) { test(ast); return true; } - bool visit(TryStatement *ast) { test(ast); return true; } - bool visit(Catch *ast) { test(ast); return true; } - bool visit(Finally *ast) { test(ast); return true; } - bool visit(FunctionExpression *ast) { test(ast); return true; } - bool visit(DebuggerStatement *ast) { test(ast); return true; } - - void test(Node *ast) - { - quint32 statementStartLine = ast->firstSourceLocation().startLine; - //Case 1/2 - if (statementStartLine <= *line && - *line <= ast->lastSourceLocation().startLine) - done = true; - - //Case 3 - if (statementStartLine > *line) { - *line = statementStartLine; - *column = ast->firstSourceLocation().startColumn; - done = true; - } - } +class QmlEnginePrivate : QmlDebugClient +{ +public: + QmlEnginePrivate(QmlEngine *engine_, QmlDebugConnection *connection_) + : QmlDebugClient(QLatin1String("V8Debugger"), connection_), + engine(engine_), + inspectorAdapter(engine, connection_), + connection(connection_) + {} + + void sendMessage(const QByteArray &msg); + void messageReceived(const QByteArray &data); + void stateChanged(State state); + + void connect(); + void disconnect(); + + void continueDebugging(StepAction stepAction); + + void evaluate(const QString expr, bool global = false, bool disableBreak = false, + int frame = -1, bool addContext = false); + void lookup(const QList<int> handles, bool includeSource = false); + void backtrace(int fromFrame = -1, int toFrame = -1, bool bottom = false); + void frame(int number = -1); + void scope(int number = -1, int frameNumber = -1); + void scripts(int types = 4, const QList<int> ids = QList<int>(), + bool includeSource = false, const QVariant filter = QVariant()); + + void setBreakpoint(const QString type, const QString target, + bool enabled = true,int line = 0, int column = 0, + const QString condition = QString(), int ignoreCount = -1); + void clearBreakpoint(int breakpoint); + void setExceptionBreak(Exceptions type, bool enabled = false); + + void version(); + void clearCache(); + + void sendAndLogV8Request(const QJsonObject &request); + + QByteArray packMessage(const QByteArray &type, const QByteArray &message = QByteArray()); + QJsonObject initObject(); + + void expandObject(const QByteArray &iname, quint64 objectId); + void flushSendBuffer(); + + void handleBacktrace(const QVariant &bodyVal, const QVariant &refsVal); + void handleLookup(const QVariant &bodyVal, const QVariant &refsVal); + void handleEvaluate(int sequence, bool success, const QVariant &bodyVal, const QVariant &refsVal); + void handleFrame(const QVariant &bodyVal, const QVariant &refsVal); + void handleScope(const QVariant &bodyVal, const QVariant &refsVal); + StackFrame extractStackFrame(const QVariant &bodyVal, const QVariant &refsVal); + + bool canEvaluateScript(const QString &script); + void updateScriptSource(const QString &fileName, int lineOffset, int columnOffset, const QString &source); - bool done; - quint32 *line; - quint32 *column; +public: + int sequence = -1; + QmlEngine *engine; + QHash<BreakpointModelId, int> breakpoints; + QHash<int, BreakpointModelId> breakpointsSync; + QList<int> breakpointsTemp; + + QHash<int, QString> evaluatingExpression; + QHash<int, QByteArray> localsAndWatchers; + QList<int> updateLocalsAndWatchers; + QList<int> debuggerCommands; + + //Cache + QList<int> currentFrameScopes; + QHash<int, int> stackIndexLookup; + + StepAction previousStepAction = Continue; + + QList<QByteArray> sendBuffer; + + QHash<QString, QTextDocument*> sourceDocuments; + QHash<QString, QWeakPointer<BaseTextEditor> > sourceEditors; + InteractiveInterpreter interpreter; + ApplicationLauncher applicationLauncher; + QmlInspectorAdapter inspectorAdapter; + QmlOutputParser outputParser; + + QTimer noDebugOutputTimer; + QHash<QString,Breakpoint> pendingBreakpoints; + QList<quint32> queryIds; + bool retryOnConnectFail = false; + bool automaticConnect = false; + + QTimer connectionTimer; + QmlDebug::QmlDebugConnection *connection; + QmlDebug::QDebugMessageClient *msgClient = 0; }; -ConsoleManagerInterface *qmlConsoleManager() +static void updateDocument(IDocument *document, const QTextDocument *textDocument) { - return ConsoleManagerInterface::instance(); + if (auto baseTextDocument = qobject_cast<TextDocument *>(document)) + baseTextDocument->document()->setPlainText(textDocument->toPlainText()); } + /////////////////////////////////////////////////////////////////////// // // QmlEngine @@ -262,94 +223,97 @@ ConsoleManagerInterface *qmlConsoleManager() /////////////////////////////////////////////////////////////////////// QmlEngine::QmlEngine(const DebuggerRunParameters &startParameters, DebuggerEngine *masterEngine) - : DebuggerEngine(startParameters) - , m_adapter(this) - , m_inspectorAdapter(&m_adapter, this) - , m_retryOnConnectFail(false) - , m_automaticConnect(false) + : DebuggerEngine(startParameters), + d(new QmlEnginePrivate(this, new QmlDebugConnection(this))) { setObjectName(QLatin1String("QmlEngine")); if (masterEngine) setMasterEngine(masterEngine); - connect(&m_adapter, SIGNAL(connectionError(QDebugSupport::Error)), - SLOT(connectionError(QDebugSupport::Error))); - connect(&m_adapter, SIGNAL(serviceConnectionError(QString)), - SLOT(serviceConnectionError(QString))); - connect(&m_adapter, SIGNAL(connected()), - SLOT(connectionEstablished())); - connect(&m_adapter, SIGNAL(connectionStartupFailed()), - SLOT(connectionStartupFailed())); - - connect(stackHandler(), SIGNAL(stackChanged()), - SLOT(updateCurrentContext())); - connect(stackHandler(), SIGNAL(currentIndexChanged()), + connect(stackHandler(), &StackHandler::stackChanged, + this, &QmlEngine::updateCurrentContext); + connect(stackHandler(), &StackHandler::currentIndexChanged, + this, &QmlEngine::updateCurrentContext); + connect(inspectorView(), SIGNAL(currentIndexChanged(QModelIndex)), SLOT(updateCurrentContext())); - connect(inspectorTreeView(), SIGNAL(currentIndexChanged(QModelIndex)), - SLOT(updateCurrentContext())); - connect(m_inspectorAdapter.agent(), SIGNAL( - expressionResult(quint32,QVariant)), - SLOT(expressionEvaluated(quint32,QVariant))); - connect(m_adapter.messageClient(), - SIGNAL(message(QtMsgType,QString, - QmlDebug::QDebugContextInfo)), - SLOT(appendDebugOutput(QtMsgType,QString, - QmlDebug::QDebugContextInfo))); - - - connect(&m_applicationLauncher, - SIGNAL(processExited(int,QProcess::ExitStatus)), - SLOT(disconnected())); - connect(&m_applicationLauncher, - SIGNAL(appendMessage(QString,Utils::OutputFormat)), - SLOT(appendMessage(QString,Utils::OutputFormat))); - connect(&m_applicationLauncher, - SIGNAL(processStarted()), - &m_noDebugOutputTimer, - SLOT(start())); - - m_outputParser.setNoOutputText(ProjectExplorer::ApplicationLauncher - ::msgWinCannotRetrieveDebuggingOutput()); - connect(&m_outputParser, SIGNAL(waitingForConnectionOnPort(quint16)), - this, SLOT(beginConnection(quint16))); - connect(&m_outputParser, SIGNAL(noOutputMessage()), - this, SLOT(tryToConnect())); - connect(&m_outputParser, SIGNAL(errorMessage(QString)), - this, SLOT(appStartupFailed(QString))); + connect(d->inspectorAdapter.agent(), &QmlInspectorAgent::expressionResult, + this, &QmlEngine::expressionEvaluated); + + connect(&d->applicationLauncher, &ApplicationLauncher::processExited, + this, &QmlEngine::disconnected); + connect(&d->applicationLauncher, &ApplicationLauncher::appendMessage, + this, &QmlEngine::appendMessage); + connect(&d->applicationLauncher, &ApplicationLauncher::processStarted, + &d->noDebugOutputTimer, static_cast<void(QTimer::*)()>(&QTimer::start)); + + d->outputParser.setNoOutputText(ApplicationLauncher::msgWinCannotRetrieveDebuggingOutput()); + connect(&d->outputParser, &QmlOutputParser::waitingForConnectionOnPort, + this, &QmlEngine::beginConnection); + connect(&d->outputParser, &QmlOutputParser::noOutputMessage, + this, [this] { tryToConnect(); }); + connect(&d->outputParser, &QmlOutputParser::errorMessage, + this, &QmlEngine::appStartupFailed); // Only wait 8 seconds for the 'Waiting for connection' on application output, // then just try to connect (application output might be redirected / blocked) - m_noDebugOutputTimer.setSingleShot(true); - m_noDebugOutputTimer.setInterval(8000); - connect(&m_noDebugOutputTimer, SIGNAL(timeout()), this, SLOT(tryToConnect())); + d->noDebugOutputTimer.setSingleShot(true); + d->noDebugOutputTimer.setInterval(8000); + connect(&d->noDebugOutputTimer, SIGNAL(timeout()), this, SLOT(tryToConnect())); - ModelManagerInterface *mmIface = ModelManagerInterface::instance(); - if (mmIface) { - connect(ModelManagerInterface::instance(), SIGNAL(documentUpdated(QmlJS::Document::Ptr)), - this, SLOT(documentUpdated(QmlJS::Document::Ptr))); + if (auto mmIface = ModelManagerInterface::instance()) { + connect(mmIface, &ModelManagerInterface::documentUpdated, + this, &QmlEngine::documentUpdated); } // we won't get any debug output if (startParameters.useTerminal) { - m_noDebugOutputTimer.setInterval(0); - m_retryOnConnectFail = true; - m_automaticConnect = true; + d->noDebugOutputTimer.setInterval(0); + d->retryOnConnectFail = true; + d->automaticConnect = true; } - if (qmlConsoleManager()) - qmlConsoleManager()->setScriptEvaluator(this); + + if (auto consoleManager = ConsoleManagerInterface::instance()) + consoleManager->setScriptEvaluator(this); + + + d->connectionTimer.setInterval(4000); + d->connectionTimer.setSingleShot(true); + connect(&d->connectionTimer, &QTimer::timeout, + this, &QmlEngine::checkConnectionState); + + connect(d->connection, &QmlDebugConnection::stateMessage, + this, &QmlEngine::showConnectionStateMessage); + connect(d->connection, &QmlDebugConnection::errorMessage, + this, &QmlEngine::showConnectionErrorMessage); + connect(d->connection, &QmlDebugConnection::error, + this, &QmlEngine::connectionErrorOccurred); + connect(d->connection, &QmlDebugConnection::opened, + &d->connectionTimer, &QTimer::stop); + connect(d->connection, &QmlDebugConnection::opened, + this, &QmlEngine::connectionEstablished); + connect(d->connection, &QmlDebugConnection::closed, + this, &QmlEngine::disconnected); + + d->msgClient = new QDebugMessageClient(d->connection); + connect(d->msgClient, &QDebugMessageClient::newState, + this, &QmlEngine::clientStateChanged); + connect(d->msgClient, &QDebugMessageClient::message, + this, &appendDebugOutput); } QmlEngine::~QmlEngine() { - QSet<Core::IDocument *> documentsToClose; + QSet<IDocument *> documentsToClose; - QHash<QString, QWeakPointer<TextEditor::BaseTextEditor> >::iterator iter; - for (iter = m_sourceEditors.begin(); iter != m_sourceEditors.end(); ++iter) { - QWeakPointer<TextEditor::BaseTextEditor> textEditPtr = iter.value(); + QHash<QString, QWeakPointer<BaseTextEditor> >::iterator iter; + for (iter = d->sourceEditors.begin(); iter != d->sourceEditors.end(); ++iter) { + QWeakPointer<BaseTextEditor> textEditPtr = iter.value(); if (textEditPtr) documentsToClose << textEditPtr.data()->document(); } - Core::EditorManager::closeDocuments(documentsToClose.toList()); + EditorManager::closeDocuments(documentsToClose.toList()); + + delete d; } void QmlEngine::setupInferior() @@ -358,7 +322,7 @@ void QmlEngine::setupInferior() notifyInferiorSetupOk(); - if (m_automaticConnect) + if (d->automaticConnect) beginConnection(); } @@ -373,7 +337,8 @@ void QmlEngine::connectionEstablished() if (!watchHandler()->watcherNames().isEmpty()) synchronizeWatchers(); - connect(watchModel(),SIGNAL(layoutChanged()),this,SLOT(synchronizeWatchers())); + connect(watchModel(), &QAbstractItemModel::layoutChanged, + this, &QmlEngine::synchronizeWatchers); if (state() == EngineRunRequested) notifyEngineRunAndInferiorRunOk(); @@ -382,13 +347,13 @@ void QmlEngine::connectionEstablished() void QmlEngine::tryToConnect(quint16 port) { showMessage(QLatin1String("QML Debugger: No application output received in time, trying to connect ..."), LogStatus); - m_retryOnConnectFail = true; + d->retryOnConnectFail = true; if (state() == EngineRunRequested) { if (isSlaveEngine()) { // Probably cpp is being debugged and hence we did not get the output yet. if (!masterEngine()->isDying()) { - m_noDebugOutputTimer.setInterval(4000); - m_noDebugOutputTimer.start(); + d->noDebugOutputTimer.setInterval(4000); + d->noDebugOutputTimer.start(); } else appStartupFailed(tr("No application output received in time")); @@ -396,15 +361,15 @@ void QmlEngine::tryToConnect(quint16 port) beginConnection(port); } } else { - m_automaticConnect = true; + d->automaticConnect = true; } } void QmlEngine::beginConnection(quint16 port) { - m_noDebugOutputTimer.stop(); + d->noDebugOutputTimer.stop(); - if (state() != EngineRunRequested && m_retryOnConnectFail) + if (state() != EngineRunRequested && d->retryOnConnectFail) return; QTC_ASSERT(state() == EngineRunRequested, return); @@ -428,19 +393,24 @@ void QmlEngine::beginConnection(quint16 port) if (runParameters().qmlServerPort > 0) port = runParameters().qmlServerPort; - m_adapter.beginConnectionTcp(host, port); -} + if (!d->connection || d->connection->isOpen()) + return; + d->connection->connectToHost(host, port); + + //A timeout to check the connection state + d->connectionTimer.start(); +} void QmlEngine::connectionStartupFailed() { - if (m_retryOnConnectFail) { + if (d->retryOnConnectFail) { // retry after 3 seconds ... QTimer::singleShot(3000, this, SLOT(beginConnection())); return; } - QMessageBox *infoBox = new QMessageBox(Core::ICore::mainWindow()); + QMessageBox *infoBox = new QMessageBox(ICore::mainWindow()); infoBox->setIcon(QMessageBox::Critical); infoBox->setWindowTitle(tr("Qt Creator")); infoBox->setText(tr("Could not connect to the in-process QML debugger." @@ -450,8 +420,8 @@ void QmlEngine::connectionStartupFailed() infoBox->setDefaultButton(QMessageBox::Retry); infoBox->setModal(true); - connect(infoBox, SIGNAL(finished(int)), - this, SLOT(errorMessageBoxFinished(int))); + connect(infoBox, &QDialog::finished, + this, &QmlEngine::errorMessageBoxFinished); infoBox->show(); } @@ -462,14 +432,14 @@ void QmlEngine::appStartupFailed(const QString &errorMessage) "\n%1").arg(errorMessage); if (isMasterEngine()) { - QMessageBox *infoBox = new QMessageBox(Core::ICore::mainWindow()); + QMessageBox *infoBox = new QMessageBox(ICore::mainWindow()); infoBox->setIcon(QMessageBox::Critical); infoBox->setWindowTitle(tr("Qt Creator")); infoBox->setText(error); infoBox->setStandardButtons(QMessageBox::Ok | QMessageBox::Help); infoBox->setDefaultButton(QMessageBox::Ok); - connect(infoBox, SIGNAL(finished(int)), - this, SLOT(errorMessageBoxFinished(int))); + connect(infoBox, &QDialog::finished, + this, &QmlEngine::errorMessageBoxFinished); infoBox->show(); } else { showMessage(error, StatusBar); @@ -486,7 +456,7 @@ void QmlEngine::errorMessageBoxFinished(int result) break; } case QMessageBox::Help: { - Core::HelpManager::handleHelpRequest(QLatin1String("qthelp://org.qt-project.qtcreator/doc/creator-debugging-qml.html")); + HelpManager::handleHelpRequest(QLatin1String("qthelp://org.qt-project.qtcreator/doc/creator-debugging-qml.html")); // fall through } default: @@ -500,37 +470,15 @@ void QmlEngine::errorMessageBoxFinished(int result) } } -void QmlEngine::connectionError(QDebugSupport::Error error) -{ - if (error == QDebugSupport::RemoteClosedConnectionError) - showMessage(tr("QML Debugger: Remote host closed connection."), StatusBar); - - if (!isSlaveEngine()) { // normal flow for slave engine when gdb exits - notifyInferiorSpontaneousStop(); - notifyInferiorIll(); - } -} - -void QmlEngine::serviceConnectionError(const QString &serviceName) -{ - showMessage(tr("QML Debugger: Could not connect to service \"%1\".") - .arg(serviceName), StatusBar); -} - -bool QmlEngine::canDisplayTooltip() const -{ - return false; -} - -void QmlEngine::filterApplicationMessage(const QString &output, int /*channel*/) +void QmlEngine::filterApplicationMessage(const QString &output, int /*channel*/) const { - m_outputParser.processOutput(output); + d->outputParser.processOutput(output); } void QmlEngine::showMessage(const QString &msg, int channel, int timeout) const { if (channel == AppOutput || channel == AppError) - const_cast<QmlEngine*>(this)->filterApplicationMessage(msg, channel); + filterApplicationMessage(msg, channel); DebuggerEngine::showMessage(msg, channel, timeout); } @@ -539,25 +487,23 @@ void QmlEngine::gotoLocation(const Location &location) const QString fileName = location.fileName(); if (QUrl(fileName).isLocalFile()) { // internal file from source files -> show generated .js - QTC_ASSERT(m_sourceDocuments.contains(fileName), return); + QTC_ASSERT(d->sourceDocuments.contains(fileName), return); QString titlePattern = tr("JS Source for %1").arg(fileName); //Check if there are open documents with the same title - foreach (Core::IDocument *document, Core::DocumentModel::openedDocuments()) { + foreach (IDocument *document, DocumentModel::openedDocuments()) { if (document->displayName() == titlePattern) { - Core::EditorManager::activateEditorForDocument(document); + EditorManager::activateEditorForDocument(document); return; } } - Core::IEditor *editor = Core::EditorManager::openEditorWithContents( + IEditor *editor = EditorManager::openEditorWithContents( QmlJSEditor::Constants::C_QMLJSEDITOR_ID, &titlePattern); if (editor) { editor->document()->setProperty(Constants::OPENED_BY_DEBUGGER, true); - QPlainTextEdit *plainTextEdit = - qobject_cast<QPlainTextEdit *>(editor->widget()); - if (plainTextEdit) + if (auto plainTextEdit = qobject_cast<QPlainTextEdit *>(editor->widget())) plainTextEdit->setReadOnly(true); - updateDocument(editor->document(), m_sourceDocuments.value(fileName)); + updateDocument(editor->document(), d->sourceDocuments.value(fileName)); } } else { DebuggerEngine::gotoLocation(location); @@ -566,8 +512,15 @@ void QmlEngine::gotoLocation(const Location &location) void QmlEngine::closeConnection() { - disconnect(watchModel(),SIGNAL(layoutChanged()),this,SLOT(synchronizeWatchers())); - m_adapter.closeConnection(); + disconnect(watchModel(), &QAbstractItemModel::layoutChanged, + this, &QmlEngine::synchronizeWatchers); + + if (d->connectionTimer.isActive()) { + d->connectionTimer.stop(); + } else { + if (d->connection) + d->connection->close(); + } } void QmlEngine::runEngine() @@ -576,25 +529,25 @@ void QmlEngine::runEngine() if (!isSlaveEngine()) { if (runParameters().startMode == AttachToRemoteServer) - m_noDebugOutputTimer.start(); + d->noDebugOutputTimer.start(); else if (runParameters().startMode == AttachToRemoteProcess) beginConnection(); else startApplicationLauncher(); } else { - m_noDebugOutputTimer.start(); + d->noDebugOutputTimer.start(); } } void QmlEngine::startApplicationLauncher() { - if (!m_applicationLauncher.isRunning()) { + if (!d->applicationLauncher.isRunning()) { appendMessage(tr("Starting %1 %2").arg( QDir::toNativeSeparators(runParameters().executable), runParameters().processArgs) + QLatin1Char('\n') , Utils::NormalMessageFormat); - m_applicationLauncher.start(ProjectExplorer::ApplicationLauncher::Gui, + d->applicationLauncher.start(ApplicationLauncher::Gui, runParameters().executable, runParameters().processArgs); } @@ -602,10 +555,10 @@ void QmlEngine::startApplicationLauncher() void QmlEngine::stopApplicationLauncher() { - if (m_applicationLauncher.isRunning()) { - disconnect(&m_applicationLauncher, SIGNAL(processExited(int,QProcess::ExitStatus)), - this, SLOT(disconnected())); - m_applicationLauncher.stop(); + if (d->applicationLauncher.isRunning()) { + disconnect(&d->applicationLauncher, &ApplicationLauncher::processExited, + this, &QmlEngine::disconnected); + d->applicationLauncher.stop(); } } @@ -622,12 +575,12 @@ void QmlEngine::notifyEngineRemoteSetupFinished(const RemoteSetupResult &result) // The remote setup can take while especialy with mixed debugging. // Just waiting for 8 seconds is not enough. Increase the timeout // to 60 s - // In case we get an output the m_outputParser will start the connection. - m_noDebugOutputTimer.setInterval(60000); + // In case we get an output the d->outputParser will start the connection. + d->noDebugOutputTimer.setInterval(60000); } else { if (isMasterEngine()) - QMessageBox::critical(Core::ICore::dialogParent(), tr("Failed to start application"), + QMessageBox::critical(ICore::dialogParent(), tr("Failed to start application"), tr("Application startup failed: %1").arg(result.reason)); notifyEngineSetupFailed(); } @@ -646,16 +599,15 @@ void QmlEngine::notifyEngineRemoteServerRunning(const QByteArray &serverChannel, notifyEngineSetupOk(); // The remote setup can take a while especially with mixed debugging. - // Just waiting for 8 seconds is not enough. Increase the timeout - // to 60 s - // In case we get an output the m_outputParser will start the connection. - m_noDebugOutputTimer.setInterval(60000); + // Just waiting for 8 seconds is not enough. Increase the timeout to 60 s. + // In case we get an output the d->outputParser will start the connection. + d->noDebugOutputTimer.setInterval(60000); } void QmlEngine::shutdownInferior() { - if (m_adapter.activeDebuggerClient()) - m_adapter.activeDebuggerClient()->endSession(); + // End session. + d->disconnect(); if (isSlaveEngine()) resetLocation(); @@ -667,12 +619,11 @@ void QmlEngine::shutdownInferior() void QmlEngine::shutdownEngine() { - if (m_adapter.activeDebuggerClient()) - m_adapter.activeDebuggerClient()->resetSession(); + clearExceptionSelection(); - if (qmlConsoleManager()) - qmlConsoleManager()->setScriptEvaluator(0); - m_noDebugOutputTimer.stop(); + if (auto consoleManager = ConsoleManagerInterface::instance()) + consoleManager->setScriptEvaluator(0); + d->noDebugOutputTimer.stop(); // double check (ill engine?): stopApplicationLauncher(); @@ -688,12 +639,12 @@ void QmlEngine::setupEngine() // we need to get the port first notifyEngineRequestRemoteSetup(); } else { - m_applicationLauncher.setEnvironment(runParameters().environment); - m_applicationLauncher.setWorkingDirectory(runParameters().workingDirectory); + d->applicationLauncher.setEnvironment(runParameters().environment); + d->applicationLauncher.setWorkingDirectory(runParameters().workingDirectory); // We can't do this in the constructore because runControl() isn't yet defined - connect(&m_applicationLauncher, SIGNAL(bringToForegroundRequested(qint64)), - runControl(), SLOT(bringApplicationToForeground(qint64)), + connect(&d->applicationLauncher, &ApplicationLauncher::bringToForegroundRequested, + runControl(), &RunControl::bringApplicationToForeground, Qt::UniqueConnection); notifyEngineSetupOk(); @@ -703,8 +654,8 @@ void QmlEngine::setupEngine() void QmlEngine::continueInferior() { QTC_ASSERT(state() == InferiorStopOk, qDebug() << state()); - if (m_adapter.activeDebuggerClient()) - m_adapter.activeDebuggerClient()->continueInferior(); + clearExceptionSelection(); + d->continueDebugging(Continue); resetLocation(); notifyInferiorRunRequested(); notifyInferiorRunOk(); @@ -712,39 +663,39 @@ void QmlEngine::continueInferior() void QmlEngine::interruptInferior() { - if (m_adapter.activeDebuggerClient()) - m_adapter.activeDebuggerClient()->interruptInferior(); + showMessage(_(INTERRUPT), LogInput); + d->sendMessage(d->packMessage(INTERRUPT)); notifyInferiorStopOk(); } void QmlEngine::executeStep() { - if (m_adapter.activeDebuggerClient()) - m_adapter.activeDebuggerClient()->executeStep(); + clearExceptionSelection(); + d->continueDebugging(StepIn); notifyInferiorRunRequested(); notifyInferiorRunOk(); } void QmlEngine::executeStepI() { - if (m_adapter.activeDebuggerClient()) - m_adapter.activeDebuggerClient()->executeStepI(); + clearExceptionSelection(); + d->continueDebugging(StepIn); notifyInferiorRunRequested(); notifyInferiorRunOk(); } void QmlEngine::executeStepOut() { - if (m_adapter.activeDebuggerClient()) - m_adapter.activeDebuggerClient()->executeStepOut(); + clearExceptionSelection(); + d->continueDebugging(StepOut); notifyInferiorRunRequested(); notifyInferiorRunOk(); } void QmlEngine::executeNext() { - if (m_adapter.activeDebuggerClient()) - m_adapter.activeDebuggerClient()->executeNext(); + clearExceptionSelection(); + d->continueDebugging(Next); notifyInferiorRunRequested(); notifyInferiorRunOk(); } @@ -765,8 +716,11 @@ void QmlEngine::executeRunToLine(const ContextData &data) bool valid; if (adjustBreakpointLineAndColumn(data.fileName, &line, &column, &valid)) modifiedData.lineNumber = line; - if (m_adapter.activeDebuggerClient()) - m_adapter.activeDebuggerClient()->executeRunToLine(modifiedData); + d->setBreakpoint(QString(_(SCRIPTREGEXP)), modifiedData.fileName, + true, modifiedData.lineNumber); + clearExceptionSelection(); + d->continueDebugging(Continue); + notifyInferiorRunRequested(); notifyInferiorRunOk(); } @@ -788,8 +742,10 @@ void QmlEngine::activateFrame(int index) if (state() != InferiorStopOk && state() != InferiorUnrunnable) return; - if (m_adapter.activeDebuggerClient()) - m_adapter.activeDebuggerClient()->activateFrame(index); + if (index != stackHandler()->currentIndex()) + d->frame(d->stackIndexLookup.value(index)); + + stackHandler()->setCurrentIndex(index); gotoLocation(stackHandler()->frames().value(index)); } @@ -811,31 +767,39 @@ void QmlEngine::insertBreakpoint(Breakpoint bp) bool valid = false; if (!adjustBreakpointLineAndColumn(params.fileName, &line, &column, &valid)) { - pendingBreakpoints.insertMulti(params.fileName, bp); + d->pendingBreakpoints.insertMulti(params.fileName, bp); return; } if (!valid) return; } - if (m_adapter.activeDebuggerClient()) { - m_adapter.activeDebuggerClient()->insertBreakpoint(bp, line, column); - } else { - foreach (BaseQmlDebuggerClient *client, m_adapter.debuggerClients()) { - client->insertBreakpoint(bp, line, column); - } + if (params.type == BreakpointAtJavaScriptThrow) { + bp.notifyBreakpointInsertOk(); + d->setExceptionBreak(AllExceptions, params.enabled); + + } else if (params.type == BreakpointByFileAndLine) { + d->setBreakpoint(QString(_(SCRIPTREGEXP)), params.fileName, + params.enabled, line, column, + QLatin1String(params.condition), params.ignoreCount); + + } else if (params.type == BreakpointOnQmlSignalEmit) { + d->setBreakpoint(QString(_(EVENT)), params.functionName, params.enabled); + bp.notifyBreakpointInsertOk(); } + + d->breakpointsSync.insert(d->sequence, bp.id()); } void QmlEngine::removeBreakpoint(Breakpoint bp) { const BreakpointParameters ¶ms = bp.parameters(); if (params.type == BreakpointByFileAndLine && - pendingBreakpoints.contains(params.fileName)) { - auto it = pendingBreakpoints.find(params.fileName); - while (it != pendingBreakpoints.end() && it.key() == params.fileName) { + d->pendingBreakpoints.contains(params.fileName)) { + auto it = d->pendingBreakpoints.find(params.fileName); + while (it != d->pendingBreakpoints.end() && it.key() == params.fileName) { if (it.value() == bp.id()) { - pendingBreakpoints.erase(it); + d->pendingBreakpoints.erase(it); return; } ++it; @@ -846,13 +810,15 @@ void QmlEngine::removeBreakpoint(Breakpoint bp) QTC_ASSERT(state == BreakpointRemoveRequested, qDebug() << bp << this << state); bp.notifyBreakpointRemoveProceeding(); - if (m_adapter.activeDebuggerClient()) { - m_adapter.activeDebuggerClient()->removeBreakpoint(bp); - } else { - foreach (BaseQmlDebuggerClient *client, m_adapter.debuggerClients()) { - client->removeBreakpoint(bp); - } - } + int breakpoint = d->breakpoints.value(bp.id()); + d->breakpoints.remove(bp.id()); + + if (params.type == BreakpointAtJavaScriptThrow) + d->setExceptionBreak(AllExceptions); + else if (params.type == BreakpointOnQmlSignalEmit) + d->setBreakpoint(QString(_(EVENT)), params.functionName, false); + else + d->clearBreakpoint(breakpoint); if (bp.state() == BreakpointRemoveProceeding) bp.notifyBreakpointRemoveOk(); @@ -864,12 +830,24 @@ void QmlEngine::changeBreakpoint(Breakpoint bp) QTC_ASSERT(state == BreakpointChangeRequested, qDebug() << bp << this << state); bp.notifyBreakpointChangeProceeding(); - if (m_adapter.activeDebuggerClient()) { - m_adapter.activeDebuggerClient()->changeBreakpoint(bp); + const BreakpointParameters ¶ms = bp.parameters(); + + BreakpointResponse br = bp.response(); + if (params.type == BreakpointAtJavaScriptThrow) { + d->setExceptionBreak(AllExceptions, params.enabled); + br.enabled = params.enabled; + bp.setResponse(br); + } else if (params.type == BreakpointOnQmlSignalEmit) { + d->setBreakpoint(QString(_(EVENT)), params.functionName, params.enabled); + br.enabled = params.enabled; + bp.setResponse(br); } else { - foreach (BaseQmlDebuggerClient *client, m_adapter.debuggerClients()) { - client->changeBreakpoint(bp); - } + //V8 supports only minimalistic changes in breakpoint + //Remove the breakpoint and add again + bp.notifyBreakpointChangeOk(); + bp.removeBreakpoint(); + BreakHandler *handler = d->engine->breakHandler(); + handler->appendBreakpoint(params); } if (bp.state() == BreakpointChangeProceeding) @@ -918,14 +896,6 @@ void QmlEngine::attemptBreakpointSynchronization() } DebuggerEngine::attemptBreakpointSynchronization(); - - if (m_adapter.activeDebuggerClient()) { - m_adapter.activeDebuggerClient()->synchronizeBreakpoints(); - } else { - foreach (BaseQmlDebuggerClient *client, m_adapter.debuggerClients()) { - client->synchronizeBreakpoints(); - } - } } bool QmlEngine::acceptsBreakpoint(Breakpoint bp) const @@ -937,10 +907,10 @@ bool QmlEngine::acceptsBreakpoint(Breakpoint bp) const //TODO: enable setting of breakpoints before start of debug session //For now, the event breakpoint can be set after the activeDebuggerClient is known //This is because the older client does not support BreakpointOnQmlSignalHandler - bool acceptBreakpoint = false; - if (m_adapter.activeDebuggerClient()) - acceptBreakpoint = m_adapter.activeDebuggerClient()->acceptsBreakpoint(bp); - return acceptBreakpoint; + BreakpointType type = bp.type(); + return type == BreakpointOnQmlSignalEmit + || type == BreakpointByFileAndLine + || type == BreakpointAtJavaScriptThrow; } void QmlEngine::loadSymbols(const QString &moduleName) @@ -958,8 +928,7 @@ void QmlEngine::reloadModules() void QmlEngine::reloadSourceFiles() { - if (m_adapter.activeDebuggerClient()) - m_adapter.activeDebuggerClient()->getSourceFiles(); + d->scripts(4, QList<int>(), true, QVariant()); } void QmlEngine::requestModuleSymbols(const QString &moduleName) @@ -967,12 +936,6 @@ void QmlEngine::requestModuleSymbols(const QString &moduleName) Q_UNUSED(moduleName) } -////////////////////////////////////////////////////////////////////// -// -// Tooltip specific stuff -// -////////////////////////////////////////////////////////////////////// - bool QmlEngine::canHandleToolTip(const DebuggerToolTipContext &) const { // This is processed by QML inspector, which has dependencies to @@ -981,20 +944,23 @@ bool QmlEngine::canHandleToolTip(const DebuggerToolTipContext &) const return true; } -////////////////////////////////////////////////////////////////////// -// -// Watch specific stuff -// -////////////////////////////////////////////////////////////////////// - void QmlEngine::assignValueInDebugger(WatchItem *item, const QString &expression, const QVariant &valueV) { if (!expression.isEmpty()) { - if (item->isInspect() && m_inspectorAdapter.agent()) - m_inspectorAdapter.agent()->assignValue(item, expression, valueV); - else if (m_adapter.activeDebuggerClient()) - m_adapter.activeDebuggerClient()->assignValueInDebugger(item, expression, valueV); + if (item->isInspect() && d->inspectorAdapter.agent()) { + d->inspectorAdapter.agent()->assignValue(item, expression, valueV); + } else { + StackHandler *handler = stackHandler(); + QString expression = QString(_("%1 = %2;")).arg(expression).arg(valueV.toString()); + if (handler->isContentsValid() && handler->currentFrame().isUsable()) { + d->evaluate(expression, false, false, handler->currentIndex()); + d->updateLocalsAndWatchers.append(d->sequence); + } else { + showMessage(QString(_("Cannot evaluate %1 in current stack frame")).arg( + expression), ConsoleOutput); + } + } } } @@ -1008,14 +974,11 @@ void QmlEngine::updateWatchData(const QByteArray &iname) return; if (item->isInspect()) { - m_inspectorAdapter.agent()->updateWatchData(*item); + d->inspectorAdapter.agent()->updateWatchData(*item); } else { - if (!item->name.isEmpty() && m_adapter.activeDebuggerClient()) { - if (item->isValueNeeded()) - m_adapter.activeDebuggerClient()->updateWatchData(*item); - if (item->isChildrenNeeded() && watchHandler()->isExpandedIName(item->iname)) { - m_adapter.activeDebuggerClient()->expandObject(item->iname, item->id); - } + if (!item->name.isEmpty()) { + if (item->isChildrenNeeded() && watchHandler()->isExpandedIName(item->iname)) + d->expandObject(item->iname, item->id); } synchronizeWatchers(); } @@ -1025,26 +988,30 @@ void QmlEngine::selectWatchData(const QByteArray &iname) { const WatchItem *item = watchHandler()->findItem(iname); if (item && item->isInspect()) - m_inspectorAdapter.agent()->watchDataSelected(item->id); + d->inspectorAdapter.agent()->watchDataSelected(item->id); } void QmlEngine::synchronizeWatchers() { - QStringList watchedExpressions = watchHandler()->watchedExpressions(); + if (state() != InferiorStopOk) + return; + + QStringList watchers = watchHandler()->watchedExpressions(); + // send watchers list - if (m_adapter.activeDebuggerClient()) { - m_adapter.activeDebuggerClient()->synchronizeWatchers(watchedExpressions); - } else { - foreach (BaseQmlDebuggerClient *client, m_adapter.debuggerClients()) - client->synchronizeWatchers(watchedExpressions); + foreach (const QString &exp, watchers) { + StackHandler *handler = stackHandler(); + if (handler->isContentsValid() && handler->currentFrame().isUsable()) { + d->evaluate(exp, false, false, handler->currentIndex()); + d->evaluatingExpression.insert(d->sequence, exp); + } } } -ConsoleItem *constructLogItemTree(ConsoleItem *parent, - const QVariant &result, - const QString &key = QString()) +static ConsoleItem *constructLogItemTree(ConsoleItem *parent, + const QVariant &result, + const QString &key = QString()) { - using namespace QmlJS; bool sorted = boolSetting(SortStructMembers); if (!result.isValid()) return 0; @@ -1086,13 +1053,10 @@ ConsoleItem *constructLogItemTree(ConsoleItem *parent, void QmlEngine::expressionEvaluated(quint32 queryId, const QVariant &result) { - if (queryIds.contains(queryId)) { - queryIds.removeOne(queryId); - using namespace QmlJS; - ConsoleManagerInterface *consoleManager = qmlConsoleManager(); - if (consoleManager) { - ConsoleItem *item = constructLogItemTree(consoleManager->rootItem(), result); - if (item) + if (d->queryIds.contains(queryId)) { + d->queryIds.removeOne(queryId); + if (auto consoleManager = ConsoleManagerInterface::instance()) { + if (ConsoleItem *item = constructLogItemTree(consoleManager->rootItem(), result)) consoleManager->printToConsolePane(item); } } @@ -1116,18 +1080,12 @@ bool QmlEngine::hasCapability(unsigned cap) const void QmlEngine::quitDebugger() { - m_noDebugOutputTimer.stop(); - m_automaticConnect = false; - m_retryOnConnectFail = false; + d->noDebugOutputTimer.stop(); + d->automaticConnect = false; + d->retryOnConnectFail = false; DebuggerEngine::quitDebugger(); } -void QmlEngine::inferiorSpontaneousStop() -{ - if (state() == InferiorRunOk) - notifyInferiorSpontaneousStop(); -} - void QmlEngine::disconnected() { showMessage(tr("QML Debugger disconnected."), StatusBar); @@ -1137,9 +1095,9 @@ void QmlEngine::disconnected() void QmlEngine::documentUpdated(Document::Ptr doc) { QString fileName = doc->fileName(); - if (pendingBreakpoints.contains(fileName)) { - QList<Breakpoint> bps = pendingBreakpoints.values(fileName); - pendingBreakpoints.remove(fileName); + if (d->pendingBreakpoints.contains(fileName)) { + QList<Breakpoint> bps = d->pendingBreakpoints.values(fileName); + d->pendingBreakpoints.remove(fileName); foreach (const Breakpoint bp, bps) insertBreakpoint(bp); } @@ -1151,13 +1109,12 @@ void QmlEngine::updateCurrentContext() if (state() == InferiorStopOk) { context = stackHandler()->currentFrame().function; } else { - QModelIndex currentIndex = inspectorTreeView()->currentIndex(); + QModelIndex currentIndex = inspectorView()->currentIndex(); const WatchData *currentData = watchHandler()->watchItem(currentIndex); if (!currentData) return; const WatchData *parentData = watchHandler()->watchItem(currentIndex.parent()); - const WatchData *grandParentData = watchHandler()->watchItem( - currentIndex.parent().parent()); + const WatchData *grandParentData = watchHandler()->watchItem(currentIndex.parent().parent()); if (currentData->id != parentData->id) context = currentData->name; else if (parentData->id != grandParentData->id) @@ -1168,44 +1125,24 @@ void QmlEngine::updateCurrentContext() synchronizeWatchers(); - ConsoleManagerInterface *consoleManager = qmlConsoleManager(); - if (consoleManager) + if (auto consoleManager = ConsoleManagerInterface::instance()) consoleManager->setContext(tr("Context:") + QLatin1Char(' ') + context); } -void QmlEngine::appendDebugOutput(QtMsgType type, const QString &message, - const QmlDebug::QDebugContextInfo &info) +void QmlEngine::executeDebuggerCommand(const QString &command, DebuggerLanguages languages) { - using namespace QmlJS; - ConsoleItem::ItemType itemType; - switch (type) { - case QtDebugMsg: - itemType = ConsoleItem::DebugType; - break; - case QtWarningMsg: - itemType = ConsoleItem::WarningType; - break; - case QtCriticalMsg: - case QtFatalMsg: - itemType = ConsoleItem::ErrorType; - break; - default: - //This case is not possible + if (!(languages & QmlLanguage)) return; - } - ConsoleManagerInterface *consoleManager = qmlConsoleManager(); - if (consoleManager) { - ConsoleItem *item = new ConsoleItem(consoleManager->rootItem(), itemType, message); - item->file = info.file; - item->line = info.line; - consoleManager->printToConsolePane(item); - } -} -void QmlEngine::executeDebuggerCommand(const QString &command, DebuggerLanguages languages) -{ - if ((languages & QmlLanguage) && m_adapter.activeDebuggerClient()) - m_adapter.activeDebuggerClient()->executeDebuggerCommand(command); + StackHandler *handler = stackHandler(); + if (handler->isContentsValid() && handler->currentFrame().isUsable()) { + d->evaluate(command, false, false, handler->currentIndex()); + d->debuggerCommands.append(d->sequence); + } else { + //Currently cannot evaluate if not in a javascript break + d->engine->showMessage(QString(_("Cannot evaluate %1 in current stack frame")).arg( + command), ConsoleOutput); + } } bool QmlEngine::evaluateScript(const QString &expression) @@ -1214,17 +1151,15 @@ bool QmlEngine::evaluateScript(const QString &expression) // Evaluate expression based on engine state // When engine->state() == InferiorStopOk, the expression is sent to debuggerClient. if (state() != InferiorStopOk) { - QModelIndex currentIndex = inspectorTreeView()->currentIndex(); - QmlInspectorAgent *agent = m_inspectorAdapter.agent(); + QModelIndex currentIndex = inspectorView()->currentIndex(); + QmlInspectorAgent *agent = d->inspectorAdapter.agent(); quint32 queryId = agent->queryExpressionResult(watchHandler()->watchItem(currentIndex)->id, expression); if (queryId) { - queryIds << queryId; + d->queryIds.append(queryId); } else { didEvaluate = false; - using namespace QmlJS; - ConsoleManagerInterface *consoleManager = qmlConsoleManager(); - if (consoleManager) { + if (auto consoleManager = ConsoleManagerInterface::instance()) { consoleManager->printToConsolePane(ConsoleItem::ErrorType, _("Error evaluating expression.")); } @@ -1235,42 +1170,15 @@ bool QmlEngine::evaluateScript(const QString &expression) return didEvaluate; } -QString QmlEngine::qmlImportPath() const -{ - return runParameters().environment.value(QLatin1String("QML_IMPORT_PATH")); -} - -void QmlEngine::logMessage(const QString &service, LogDirection direction, const QString &message) -{ - QString msg = service; - msg += direction == LogSend ? QLatin1String(": sending ") : QLatin1String(": receiving "); - msg += message; - showMessage(msg, LogDebug); -} - -void QmlEngine::setSourceFiles(const QStringList &fileNames) -{ - QMap<QString,QString> files; - foreach (const QString &file, fileNames) { - QString shortName = file; - QString fullName = toFileInProject(file); - files.insert(shortName, fullName); - } - - sourceFilesHandler()->setSourceFiles(files); - //update open editors - -} - -void QmlEngine::updateScriptSource(const QString &fileName, int lineOffset, int columnOffset, - const QString &source) +void QmlEnginePrivate::updateScriptSource(const QString &fileName, int lineOffset, int columnOffset, + const QString &source) { QTextDocument *document = 0; - if (m_sourceDocuments.contains(fileName)) { - document = m_sourceDocuments.value(fileName); + if (sourceDocuments.contains(fileName)) { + document = sourceDocuments.value(fileName); } else { document = new QTextDocument(this); - m_sourceDocuments.insert(fileName, document); + sourceDocuments.insert(fileName, document); } // We're getting an unordered set of snippets that can even interleave @@ -1307,7 +1215,7 @@ void QmlEngine::updateScriptSource(const QString &fileName, int lineOffset, int //update open editors QString titlePattern = tr("JS Source for %1").arg(fileName); //Check if there are open editors with the same title - foreach (Core::IDocument *doc, Core::DocumentModel::openedDocuments()) { + foreach (IDocument *doc, DocumentModel::openedDocuments()) { if (doc->displayName() == titlePattern) { updateDocument(doc, document); break; @@ -1315,45 +1223,1446 @@ void QmlEngine::updateScriptSource(const QString &fileName, int lineOffset, int } } -void QmlEngine::updateDocument(Core::IDocument *document, const QTextDocument *textDocument) +bool QmlEnginePrivate::canEvaluateScript(const QString &script) +{ + interpreter.clearText(); + interpreter.appendText(script); + return interpreter.canEvaluate(); +} + +void QmlEngine::connectionErrorOccurred(QDebugSupport::Error error) +{ + // this is only an error if we are already connected and something goes wrong. + if (isConnected()) { + if (error == QDebugSupport::RemoteClosedConnectionError) + showMessage(tr("QML Debugger: Remote host closed connection."), StatusBar); + + if (!isSlaveEngine()) { // normal flow for slave engine when gdb exits + notifyInferiorSpontaneousStop(); + notifyInferiorIll(); + } + } else { + d->connectionTimer.stop(); + connectionStartupFailed(); + } +} + +void QmlEngine::clientStateChanged(QmlDebugClient::State state) +{ + QString serviceName; + float version = 0; + if (QmlDebugClient *client = qobject_cast<QmlDebugClient*>(sender())) { + serviceName = client->name(); + version = client->remoteVersion(); + } + + logServiceStateChange(serviceName, version, state); +} + +void QmlEngine::checkConnectionState() +{ + if (!isConnected()) { + closeConnection(); + connectionStartupFailed(); + } +} + +bool QmlEngine::isConnected() const +{ + return d->connection->isOpen(); +} + +void QmlEngine::showConnectionStateMessage(const QString &message) +{ + showMessage(_("QML Debugger: ") + message, LogStatus); +} + +void QmlEngine::showConnectionErrorMessage(const QString &message) +{ + showMessage(_("QML Debugger: ") + message, LogError); +} + +void QmlEngine::logServiceStateChange(const QString &service, float version, + QmlDebugClient::State newState) +{ + switch (newState) { + case QmlDebugClient::Unavailable: { + showConnectionStateMessage(_("Status of \"%1\" Version: %2 changed to 'unavailable'."). + arg(service).arg(QString::number(version))); + break; + } + case QmlDebugClient::Enabled: { + showConnectionStateMessage(_("Status of \"%1\" Version: %2 changed to 'enabled'."). + arg(service).arg(QString::number(version))); + break; + } + + case QmlDebugClient::NotConnected: { + showConnectionStateMessage(_("Status of \"%1\" Version: %2 changed to 'not connected'."). + arg(service).arg(QString::number(version))); + break; + } + } +} + +void QmlEngine::logServiceActivity(const QString &service, const QString &logMessage) +{ + showMessage(service + QLatin1Char(' ') + logMessage, LogDebug); +} + +void QmlEnginePrivate::connect() +{ + engine->showMessage(_(CONNECT), LogInput); + sendMessage(packMessage(CONNECT)); +} + +void QmlEnginePrivate::disconnect() +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "disconnect", + // } + QJsonObject jsonVal = initObject(); + jsonVal.insert(_(COMMAND), _(DISCONNECT)); + + const QByteArray msg = QJsonDocument(jsonVal).toJson(QJsonDocument::Compact); + engine->showMessage(QString::fromUtf8(msg), LogInput); + sendMessage(packMessage(DISCONNECT, msg)); +} + +void QmlEnginePrivate::continueDebugging(StepAction action) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "continue", + // "arguments" : { "stepaction" : <"in", "next" or "out">, + // "stepcount" : <number of steps (default 1)> + // } + // } + QJsonObject jsonVal = initObject(); + jsonVal.insert(_(COMMAND), _(CONTINEDEBUGGING)); + + if (action != Continue) { + QJsonObject args; + switch (action) { + case StepIn: + args.insert(_(STEPACTION), _(IN)); + break; + case StepOut: + args.insert(_(STEPACTION), _(OUT)); + break; + case Next: + args.insert(_(STEPACTION), _(NEXT)); + break; + default:break; + } + + jsonVal.insert(_(ARGUMENTS), args); + } + sendAndLogV8Request(jsonVal); + previousStepAction = action; +} + +void QmlEnginePrivate::evaluate(const QString expr, bool global, + bool disableBreak, int frame, bool addContext) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "evaluate", + // "arguments" : { "expression" : <expression to evaluate>, + // "frame" : <number>, + // "global" : <boolean>, + // "disable_break" : <boolean>, + // "additional_context" : [ + // { "name" : <name1>, "handle" : <handle1> }, + // { "name" : <name2>, "handle" : <handle2> }, + // ... + // ] + // } + // } + QJsonObject jsonVal = initObject(); + jsonVal.insert(_(COMMAND), _(EVALUATE)); + + QJsonObject args { + { _(EXPRESSION), expr } + }; + + if (frame != -1) + args.insert(_(FRAME), frame); + + if (global) + args.insert(_(GLOBAL), global); + + if (disableBreak) + args.insert(_(DISABLE_BREAK), disableBreak); + + if (addContext) { + WatchHandler *watchHandler = engine->watchHandler(); + QAbstractItemModel *watchModel = watchHandler->model(); + int rowCount = watchModel->rowCount(); + + QJsonArray ctxtList; + while (rowCount) { + QModelIndex index = watchModel->index(--rowCount, 0); + const WatchData *data = watchHandler->watchItem(index); + const QJsonObject ctxt { + { _(NAME), data->name }, + { _(HANDLE), int(data->id) } + }; + + ctxtList.push_front(ctxt); + } + + args.insert(_(ADDITIONAL_CONTEXT), ctxtList); + } + + jsonVal.insert(_(ARGUMENTS), args); + + sendAndLogV8Request(jsonVal); +} + +void QmlEnginePrivate::lookup(QList<int> handles, bool includeSource) { - TextEditor::TextDocument *baseTextDocument - = qobject_cast<TextEditor::TextDocument *>(document); - if (!baseTextDocument) + // { "seq" : <number>, + // "type" : "request", + // "command" : "lookup", + // "arguments" : { "handles" : <array of handles>, + // "includeSource" : <boolean indicating whether + // the source will be included when + // script objects are returned>, + // } + // } + QJsonObject jsonVal = initObject(); + jsonVal.insert(_(COMMAND), _(LOOKUP)); + + QJsonObject args; + + QJsonArray array; + foreach (int handle, handles) + array.push_back(handle); + args.insert(_(HANDLES), array); + + if (includeSource) + args.insert(_(INCLUDESOURCE), includeSource); + + jsonVal.insert(_(ARGUMENTS), args); + + sendAndLogV8Request(jsonVal); +} + +void QmlEnginePrivate::backtrace(int fromFrame, int toFrame, bool bottom) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "backtrace", + // "arguments" : { "fromFrame" : <number> + // "toFrame" : <number> + // "bottom" : <boolean, set to true if the bottom of the + // stack is requested> + // } + // } + QJsonObject jsonVal = initObject(); + jsonVal.insert(_(COMMAND), _(BACKTRACE)); + + QJsonObject args; + + if (fromFrame != -1) + args.insert(_(FROMFRAME), fromFrame); + + if (toFrame != -1) + args.insert(_(TOFRAME), toFrame); + + if (bottom) + args.insert(_(BOTTOM), bottom); + + jsonVal.insert(_(ARGUMENTS), args); + + sendAndLogV8Request(jsonVal); +} + +void QmlEnginePrivate::frame(int number) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "frame", + // "arguments" : { "number" : <frame number> + // } + // } + QJsonObject jsonVal = initObject(); + jsonVal.insert(_(COMMAND), _(FRAME)); + + if (number != -1) { + const QJsonObject args { + { _(NUMBER), number } + }; + + jsonVal.insert(_(ARGUMENTS), args); + } + + sendAndLogV8Request(jsonVal); +} + +void QmlEnginePrivate::scope(int number, int frameNumber) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "scope", + // "arguments" : { "number" : <scope number> + // "frameNumber" : <frame number, optional uses selected + // frame if missing> + // } + // } + QJsonObject jsonVal = initObject(); + jsonVal.insert(_(COMMAND), _(SCOPE)); + + if (number != -1) { + QJsonObject args { + { _(NUMBER), number } + }; + + if (frameNumber != -1) + args.insert(_(FRAMENUMBER), frameNumber); + + jsonVal.insert(_(ARGUMENTS), args); + } + + sendAndLogV8Request(jsonVal); +} + +void QmlEnginePrivate::scripts(int types, const QList<int> ids, bool includeSource, + const QVariant filter) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "scripts", + // "arguments" : { "types" : <types of scripts to retrieve + // set bit 0 for native scripts + // set bit 1 for extension scripts + // set bit 2 for normal scripts + // (default is 4 for normal scripts)> + // "ids" : <array of id's of scripts to return. If this is not specified all scripts are requrned> + // "includeSource" : <boolean indicating whether the source code should be included for the scripts returned> + // "filter" : <string or number: filter string or script id. + // If a number is specified, then only the script with the same number as its script id will be retrieved. + // If a string is specified, then only scripts whose names contain the filter string will be retrieved.> + // } + // } + QJsonObject jsonVal = initObject(); + jsonVal.insert(_(COMMAND), _(SCRIPTS)); + + QJsonObject args { + { _(TYPES), types } + }; + + if (ids.count()) { + QJsonArray array; + foreach (int id, ids) { + array.push_back(id); + } + args.insert(_(IDS), array); + } + + if (includeSource) + args.insert(_(INCLUDESOURCE), includeSource); + + QJsonValue filterValue; + if (filter.type() == QVariant::String) + filterValue = filter.toString(); + else if (filter.type() == QVariant::Int) + filterValue = filter.toInt(); + else + QTC_CHECK(!filter.isValid()); + + args.insert(_(FILTER), filterValue); + + jsonVal.insert(_(ARGUMENTS), args); + + sendAndLogV8Request(jsonVal); +} + +void QmlEnginePrivate::setBreakpoint(const QString type, const QString target, + bool enabled, int line, int column, + const QString condition, int ignoreCount) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "setbreakpoint", + // "arguments" : { "type" : <"function" or "script" or "scriptId" or "scriptRegExp"> + // "target" : <function expression or script identification> + // "line" : <line in script or function> + // "column" : <character position within the line> + // "enabled" : <initial enabled state. True or false, default is true> + // "condition" : <string with break point condition> + // "ignoreCount" : <number specifying the number of break point hits to ignore, default value is 0> + // } + // } + if (type == _(EVENT)) { + QByteArray params; + QmlDebugStream rs(¶ms, QIODevice::WriteOnly); + rs << target.toUtf8() << enabled; + engine->showMessage(QString(_("%1 %2 %3")).arg(_(BREAKONSIGNAL), target, enabled ? _("enabled") : _("disabled")), LogInput); + sendMessage(packMessage(BREAKONSIGNAL, params)); + + } else { + QJsonObject jsonVal = initObject(); + jsonVal.insert(_(COMMAND), _(SETBREAKPOINT)); + + QJsonObject args { + { _(TYPE), type }, + { _(ENABLED), enabled } + }; + if (type == _(SCRIPTREGEXP)) + args.insert(_(TARGET), Utils::FileName::fromString(target).fileName()); + else + args.insert(_(TARGET), target); + + if (line) + args.insert(_(LINE), line - 1); + + if (column) + args.insert(_(COLUMN), column - 1); + + if (!condition.isEmpty()) + args.insert(_(CONDITION), condition); + + if (ignoreCount != -1) + args.insert(_(IGNORECOUNT), ignoreCount); + + jsonVal.insert(_(ARGUMENTS), args); + + sendAndLogV8Request(jsonVal); + } +} + +void QmlEnginePrivate::clearBreakpoint(int breakpoint) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "clearbreakpoint", + // "arguments" : { "breakpoint" : <number of the break point to clear> + // } + // } + QJsonObject jsonVal = initObject(); + jsonVal.insert(_(COMMAND), _(CLEARBREAKPOINT)); + + QJsonObject args { + { _(BREAKPOINT), breakpoint } + }; + + jsonVal.insert(_(ARGUMENTS), args); + + sendAndLogV8Request(jsonVal); +} + +void QmlEnginePrivate::setExceptionBreak(Exceptions type, bool enabled) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "setexceptionbreak", + // "arguments" : { "type" : <string: "all", or "uncaught">, + // "enabled" : <optional bool: enables the break type if true> + // } + // } + QJsonObject jsonVal = initObject(); + jsonVal.insert(_(COMMAND), _(SETEXCEPTIONBREAK)); + + QJsonObject args; + + if (type == AllExceptions) + args.insert(_(TYPE), _(ALL)); + //Not Supported + // else if (type == UncaughtExceptions) + // args.setProperty(_(TYPE),QScriptValue(_(UNCAUGHT))); + + if (enabled) + args.insert(_(ENABLED), enabled); + + jsonVal.insert(_(ARGUMENTS), args); + + sendAndLogV8Request(jsonVal); +} + +void QmlEnginePrivate::version() +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "version", + // } + QJsonObject jsonVal = initObject(); + jsonVal.insert(_(COMMAND), _(VERSION)); + + sendAndLogV8Request(jsonVal); +} + +QVariant valueFromRef(int handle, const QVariant &refsVal, bool *success) +{ + *success = false; + QVariant variant; + const QVariantList refs = refsVal.toList(); + foreach (const QVariant &ref, refs) { + const QVariantMap refData = ref.toMap(); + if (refData.value(_(HANDLE)).toInt() == handle) { + variant = refData; + *success = true; + break; + } + } + return variant; +} + +QmlV8ObjectData extractData(const QVariant &data, const QVariant &refsVal) +{ + // { "handle" : <handle>, + // "type" : <"undefined", "null", "boolean", "number", "string", "object", "function" or "frame"> + // } + + // {"handle":<handle>,"type":"undefined"} + + // {"handle":<handle>,"type":"null"} + + // { "handle":<handle>, + // "type" : <"boolean", "number" or "string"> + // "value" : <JSON encoded value> + // } + + // {"handle":7,"type":"boolean","value":true} + + // {"handle":8,"type":"number","value":42} + + // { "handle" : <handle>, + // "type" : "object", + // "className" : <Class name, ECMA-262 property [[Class]]>, + // "constructorFunction" : {"ref":<handle>}, + // "protoObject" : {"ref":<handle>}, + // "prototypeObject" : {"ref":<handle>}, + // "properties" : [ {"name" : <name>, + // "ref" : <handle> + // }, + // ... + // ] + // } + + // { "handle" : <handle>, + // "type" : "function", + // "className" : "Function", + // "constructorFunction" : {"ref":<handle>}, + // "protoObject" : {"ref":<handle>}, + // "prototypeObject" : {"ref":<handle>}, + // "name" : <function name>, + // "inferredName" : <inferred function name for anonymous functions> + // "source" : <function source>, + // "script" : <reference to function script>, + // "scriptId" : <id of function script>, + // "position" : <function begin position in script>, + // "line" : <function begin source line in script>, + // "column" : <function begin source column in script>, + // "properties" : [ {"name" : <name>, + // "ref" : <handle> + // }, + // ... + // ] + // } + + QmlV8ObjectData objectData; + const QVariantMap dataMap = data.toMap(); + + objectData.name = dataMap.value(_(NAME)).toByteArray(); + + if (dataMap.contains(_(REF))) { + objectData.handle = dataMap.value(_(REF)).toInt(); + bool success; + QVariant dataFromRef = valueFromRef(objectData.handle, refsVal, &success); + if (success) { + QmlV8ObjectData data = extractData(dataFromRef, refsVal); + objectData.type = data.type; + objectData.value = data.value; + objectData.properties = data.properties; + } + } else { + objectData.handle = dataMap.value(_(HANDLE)).toInt(); + QString type = dataMap.value(_(TYPE)).toString(); + + if (type == _("undefined")) { + objectData.type = QByteArray("undefined"); + objectData.value = QVariant(_("undefined")); + + } else if (type == _("null")) { + objectData.type = QByteArray("null"); + objectData.value= QVariant(_("null")); + + } else if (type == _("boolean")) { + objectData.type = QByteArray("boolean"); + objectData.value = dataMap.value(_(VALUE)); + + } else if (type == _("number")) { + objectData.type = QByteArray("number"); + objectData.value = dataMap.value(_(VALUE)); + + } else if (type == _("string")) { + objectData.type = QByteArray("string"); + objectData.value = dataMap.value(_(VALUE)); + + } else if (type == _("object")) { + objectData.type = QByteArray("object"); + objectData.value = dataMap.value(_("className")); + objectData.properties = dataMap.value(_("properties")).toList(); + + } else if (type == _("function")) { + objectData.type = QByteArray("function"); + objectData.value = dataMap.value(_(NAME)); + objectData.properties = dataMap.value(_("properties")).toList(); + + } else if (type == _("script")) { + objectData.type = QByteArray("script"); + objectData.value = dataMap.value(_(NAME)); + } + } + + return objectData; +} + +void QmlEnginePrivate::clearCache() +{ + currentFrameScopes.clear(); + updateLocalsAndWatchers.clear(); +} + +QByteArray QmlEnginePrivate::packMessage(const QByteArray &type, const QByteArray &message) +{ + SDEBUG(message); + QByteArray request; + QmlDebugStream rs(&request, QIODevice::WriteOnly); + QByteArray cmd = V8DEBUG; + rs << cmd << type << message; + return request; +} + +QJsonObject QmlEnginePrivate::initObject() +{ + return QJsonObject { + {_(SEQ), ++sequence}, + {_(TYPE), _(REQUEST)} + }; +} + +void QmlEnginePrivate::sendAndLogV8Request(const QJsonObject &request) +{ + const QByteArray msg = QJsonDocument(request).toJson(QJsonDocument::Compact); + engine->showMessage(QString::fromLatin1("%1 %2").arg(_(V8REQUEST), QString::fromUtf8(msg)), LogInput); + sendMessage(packMessage(V8REQUEST, msg)); +} + +void QmlEnginePrivate::expandObject(const QByteArray &iname, quint64 objectId) +{ + if (objectId == 0) { + //We may have got the global object + const WatchItem *watch = engine->watchHandler()->findItem(iname); + if (watch->value == QLatin1String("global")) { + StackHandler *stackHandler = engine->stackHandler(); + if (stackHandler->isContentsValid() && stackHandler->currentFrame().isUsable()) { + evaluate(watch->name, false, false, stackHandler->currentIndex()); + evaluatingExpression.insert(sequence, QLatin1String(iname)); + } + return; + } + } + localsAndWatchers.insertMulti(objectId, iname); + lookup(QList<int>() << objectId); +} + +void QmlEnginePrivate::messageReceived(const QByteArray &data) +{ + QmlDebugStream ds(data); + QByteArray command; + ds >> command; + + if (command == V8DEBUG) { + QByteArray type; + QByteArray response; + ds >> type >> response; + + engine->showMessage(QLatin1String(type), LogOutput); + if (type == CONNECT) { + //debugging session started + + } else if (type == INTERRUPT) { + //debug break requested + + } else if (type == BREAKONSIGNAL) { + //break on signal handler requested + + } else if (type == V8MESSAGE) { + const QString responseString = QLatin1String(response); + SDEBUG(responseString); + engine->showMessage(QLatin1String(V8MESSAGE) + QLatin1Char(' ') + responseString, LogOutput); + + const QVariantMap resp = + QJsonDocument::fromJson(responseString.toUtf8()).toVariant().toMap(); + + const QString type(resp.value(_(TYPE)).toString()); + + if (type == _("response")) { + + bool success = resp.value(_("success")).toBool(); + if (!success) { + SDEBUG("Request was unsuccessful"); + } + + const QString debugCommand(resp.value(_(COMMAND)).toString()); + + if (debugCommand == _(DISCONNECT)) { + //debugging session ended + + } else if (debugCommand == _(CONTINEDEBUGGING)) { + //do nothing, wait for next break + + } else if (debugCommand == _(BACKTRACE)) { + if (success) + handleBacktrace(resp.value(_(BODY)), resp.value(_(REFS))); + + } else if (debugCommand == _(LOOKUP)) { + if (success) + handleLookup(resp.value(_(BODY)), resp.value(_(REFS))); + + } else if (debugCommand == _(EVALUATE)) { + int seq = resp.value(_("request_seq")).toInt(); + if (success) { + handleEvaluate(seq, success, resp.value(_(BODY)), resp.value(_(REFS))); + } else { + QVariantMap map; + map.insert(_(TYPE), QVariant(_("string"))); + map.insert(_(VALUE), resp.value(_("message"))); + handleEvaluate(seq, success, QVariant(map), QVariant()); + } + + } else if (debugCommand == _(SETBREAKPOINT)) { + // { "seq" : <number>, + // "type" : "response", + // "request_seq" : <number>, + // "command" : "setbreakpoint", + // "body" : { "type" : <"function" or "script"> + // "breakpoint" : <break point number of the new break point> + // } + // "running" : <is the VM running after sending this response> + // "success" : true + // } + + int seq = resp.value(_("request_seq")).toInt(); + const QVariantMap breakpointData = resp.value(_(BODY)).toMap(); + int index = breakpointData.value(_("breakpoint")).toInt(); + + if (breakpointsSync.contains(seq)) { + BreakpointModelId id = breakpointsSync.take(seq); + breakpoints.insert(id, index); + + //Is actual position info present? Then breakpoint was + //accepted + const QVariantList actualLocations = + breakpointData.value(_("actual_locations")).toList(); + if (actualLocations.count()) { + //The breakpoint requested line should be same as + //actual line + BreakHandler *handler = engine->breakHandler(); + Breakpoint bp = handler->breakpointById(id); + if (bp.state() != BreakpointInserted) { + BreakpointResponse br = bp.response(); + br.lineNumber = breakpointData.value(_("line")).toInt() + 1; + bp.setResponse(br); + bp.notifyBreakpointInsertOk(); + } + } + + + } else { + breakpointsTemp.append(index); + } + + + } else if (debugCommand == _(CLEARBREAKPOINT)) { + // DO NOTHING + + } else if (debugCommand == _(SETEXCEPTIONBREAK)) { + // { "seq" : <number>, + // "type" : "response", + // "request_seq" : <number>, + // "command" : "setexceptionbreak", + // "body" : { "type" : <string: "all" or "uncaught" corresponding to the request.>, + // "enabled" : <bool: true if the break type is currently enabled as a result of the request> + // } + // "running" : true + // "success" : true + // } + + + } else if (debugCommand == _(FRAME)) { + if (success) + handleFrame(resp.value(_(BODY)), resp.value(_(REFS))); + + } else if (debugCommand == _(SCOPE)) { + if (success) + handleScope(resp.value(_(BODY)), resp.value(_(REFS))); + + } else if (debugCommand == _(SCRIPTS)) { + // { "seq" : <number>, + // "type" : "response", + // "request_seq" : <number>, + // "command" : "scripts", + // "body" : [ { "name" : <name of the script>, + // "id" : <id of the script> + // "lineOffset" : <line offset within the containing resource> + // "columnOffset" : <column offset within the containing resource> + // "lineCount" : <number of lines in the script> + // "data" : <optional data object added through the API> + // "source" : <source of the script if includeSource was specified in the request> + // "sourceStart" : <first 80 characters of the script if includeSource was not specified in the request> + // "sourceLength" : <total length of the script in characters> + // "scriptType" : <script type (see request for values)> + // "compilationType" : < How was this script compiled: + // 0 if script was compiled through the API + // 1 if script was compiled through eval + // > + // "evalFromScript" : <if "compilationType" is 1 this is the script from where eval was called> + // "evalFromLocation" : { line : < if "compilationType" is 1 this is the line in the script from where eval was called> + // column : < if "compilationType" is 1 this is the column in the script from where eval was called> + // ] + // "running" : <is the VM running after sending this response> + // "success" : true + // } + + if (success) { + const QVariantList body = resp.value(_(BODY)).toList(); + + QStringList sourceFiles; + for (int i = 0; i < body.size(); ++i) { + const QVariantMap entryMap = body.at(i).toMap(); + const int lineOffset = entryMap.value(QLatin1String("lineOffset")).toInt(); + const int columnOffset = entryMap.value(QLatin1String("columnOffset")).toInt(); + const QString name = entryMap.value(QLatin1String("name")).toString(); + const QString source = entryMap.value(QLatin1String("source")).toString(); + + if (name.isEmpty()) + continue; + + if (!sourceFiles.contains(name)) + sourceFiles << name; + + updateScriptSource(name, lineOffset, columnOffset, source); + } + + QMap<QString,QString> files; + foreach (const QString &file, sourceFiles) { + QString shortName = file; + QString fullName = engine->toFileInProject(file); + files.insert(shortName, fullName); + } + + engine->sourceFilesHandler()->setSourceFiles(files); + //update open editors + } + } else if (debugCommand == _(VERSION)) { + engine->showMessage(QString(_("Using V8 Version: %1")).arg( + resp.value(_(BODY)).toMap(). + value(_("V8Version")).toString()), LogOutput); + + } else { + // DO NOTHING + } + + } else if (type == _(EVENT)) { + const QString eventType(resp.value(_(EVENT)).toString()); + + if (eventType == _("break")) { + const QVariantMap breakData = resp.value(_(BODY)).toMap(); + const QString invocationText = breakData.value(_("invocationText")).toString(); + const QString scriptUrl = breakData.value(_("script")).toMap().value(_("name")).toString(); + const QString sourceLineText = breakData.value(_("sourceLineText")).toString(); + + bool inferiorStop = true; + + QList<int> v8BreakpointIds; + { + const QVariantList v8BreakpointIdList = breakData.value(_("breakpoints")).toList(); + foreach (const QVariant &breakpointId, v8BreakpointIdList) + v8BreakpointIds << breakpointId.toInt(); + } + + if (!v8BreakpointIds.isEmpty() && invocationText.startsWith(_("[anonymous]()")) + && scriptUrl.endsWith(_(".qml")) + && sourceLineText.trimmed().startsWith(QLatin1Char('('))) { + + // we hit most likely the anonymous wrapper function automatically generated for bindings + // -> relocate the breakpoint to column: 1 and continue + + int newColumn = sourceLineText.indexOf(QLatin1Char('(')) + 1; + BreakHandler *handler = engine->breakHandler(); + + foreach (int v8Id, v8BreakpointIds) { + const BreakpointModelId id = breakpoints.key(v8Id); + Breakpoint bp = handler->breakpointById(id); + if (bp.isValid()) { + const BreakpointParameters ¶ms = bp.parameters(); + + clearBreakpoint(v8Id); + setBreakpoint(QString(_(SCRIPTREGEXP)), + params.fileName, + params.enabled, + params.lineNumber, + newColumn, + QString(QString::fromLatin1(params.condition)), + params.ignoreCount); + breakpointsSync.insert(sequence, id); + } + } + continueDebugging(Continue); + inferiorStop = false; + } + + //Skip debug break if this is an internal function + if (sourceLineText == _(INTERNAL_FUNCTION)) { + continueDebugging(previousStepAction); + inferiorStop = false; + } + + if (inferiorStop) { + //Update breakpoint data + BreakHandler *handler = engine->breakHandler(); + foreach (int v8Id, v8BreakpointIds) { + const BreakpointModelId id = breakpoints.key(v8Id); + Breakpoint bp = handler->breakpointById(id); + if (bp) { + BreakpointResponse br = bp.response(); + if (br.functionName.isEmpty()) { + br.functionName = invocationText; + bp.setResponse(br); + } + if (bp.state() != BreakpointInserted) { + br.lineNumber = breakData.value( + _("sourceLine")).toInt() + 1; + bp.setResponse(br); + bp.notifyBreakpointInsertOk(); + } + } + } + + if (engine->state() == InferiorRunOk) { + foreach (const QVariant &breakpointId, v8BreakpointIds) { + if (breakpointsTemp.contains(breakpointId.toInt())) + clearBreakpoint(breakpointId.toInt()); + } + engine->notifyInferiorSpontaneousStop(); + backtrace(); + } else if (engine->state() == InferiorStopOk) { + backtrace(); + } + } + + } else if (eventType == _("exception")) { + const QVariantMap body = resp.value(_(BODY)).toMap(); + int lineNumber = body.value(_("sourceLine")).toInt() + 1; + + const QVariantMap script = body.value(_("script")).toMap(); + QUrl fileUrl(script.value(_(NAME)).toString()); + QString filePath = engine->toFileInProject(fileUrl); + + const QVariantMap exception = body.value(_("exception")).toMap(); + QString errorMessage = exception.value(_("text")).toString(); + + QStringList messages = highlightExceptionCode(lineNumber, filePath, errorMessage); + foreach (const QString msg, messages) + engine->showMessage(msg, ConsoleOutput); + + if (engine->state() == InferiorRunOk) { + engine->notifyInferiorSpontaneousStop(); + backtrace(); + } + + if (engine->state() == InferiorStopOk) + backtrace(); + + } else if (eventType == _("afterCompile")) { + //Currently break point relocation is disabled. + //Uncomment the line below when it will be enabled. +// listBreakpoints(); + } + + //Sometimes we do not get event type! + //This is most probably due to a wrong eval expression. + //Redirect output to console. + if (eventType.isEmpty()) { + bool success = resp.value(_("success")).toBool(); + QVariantMap map; + map.insert(_(TYPE), QVariant(_("string"))); + map.insert(_(VALUE), resp.value(_("message"))); + //Since there is no sequence value, best estimate is + //last sequence value + handleEvaluate(sequence, success, QVariant(map), QVariant()); + } + + } //EVENT + } //V8MESSAGE + + } else { + //DO NOTHING + } +} + +void QmlEnginePrivate::handleBacktrace(const QVariant &bodyVal, const QVariant &refsVal) +{ + // { "seq" : <number>, + // "type" : "response", + // "request_seq" : <number>, + // "command" : "backtrace", + // "body" : { "fromFrame" : <number> + // "toFrame" : <number> + // "totalFrames" : <number> + // "frames" : <array of frames - see frame request for details> + // } + // "running" : <is the VM running after sending this response> + // "success" : true + // } + + const QVariantMap body = bodyVal.toMap(); + const QVariantList frames = body.value(_("frames")).toList(); + + int fromFrameIndex = body.value(_("fromFrame")).toInt(); + + QTC_ASSERT(0 == fromFrameIndex, return); + + StackHandler *stackHandler = engine->stackHandler(); + StackFrames stackFrames; + int i = 0; + stackIndexLookup.clear(); + foreach (const QVariant &frame, frames) { + StackFrame stackFrame = extractStackFrame(frame, refsVal); + if (stackFrame.level < 0) + continue; + stackIndexLookup.insert(i, stackFrame.level); + stackFrame.level = i; + stackFrames << stackFrame; + i++; + } + stackHandler->setFrames(stackFrames); + + //Populate locals and watchers wrt top frame + //Update all Locals visible in current scope + //Traverse the scope chain and store the local properties + //in a list and show them in the Locals Window. + handleFrame(frames.value(0), refsVal); +} + +StackFrame QmlEnginePrivate::extractStackFrame(const QVariant &bodyVal, const QVariant &refsVal) +{ + // { "seq" : <number>, + // "type" : "response", + // "request_seq" : <number>, + // "command" : "frame", + // "body" : { "index" : <frame number>, + // "receiver" : <frame receiver>, + // "func" : <function invoked>, + // "script" : <script for the function>, + // "constructCall" : <boolean indicating whether the function was called as constructor>, + // "debuggerFrame" : <boolean indicating whether this is an internal debugger frame>, + // "arguments" : [ { name: <name of the argument - missing of anonymous argument>, + // value: <value of the argument> + // }, + // ... <the array contains all the arguments> + // ], + // "locals" : [ { name: <name of the local variable>, + // value: <value of the local variable> + // }, + // ... <the array contains all the locals> + // ], + // "position" : <source position>, + // "line" : <source line>, + // "column" : <source column within the line>, + // "sourceLineText" : <text for current source line>, + // "scopes" : [ <array of scopes, see scope request below for format> ], + + // } + // "running" : <is the VM running after sending this response> + // "success" : true + // } + + const QVariantMap body = bodyVal.toMap(); + + StackFrame stackFrame; + stackFrame.level = body.value(_("index")).toInt(); + //Do not insert the frame corresponding to the internal function + if (body.value(QLatin1String("sourceLineText")) == QLatin1String(INTERNAL_FUNCTION)) { + stackFrame.level = -1; + return stackFrame; + } + + QmlV8ObjectData objectData = extractData(body.value(_("func")), refsVal); + QString functionName = objectData.value.toString(); + if (functionName.isEmpty()) + functionName = tr("Anonymous Function"); + stackFrame.function = functionName; + + objectData = extractData(body.value(_("script")), refsVal); + stackFrame.file = engine->toFileInProject(objectData.value.toString()); + stackFrame.usable = QFileInfo(stackFrame.file).isReadable(); + + objectData = extractData(body.value(_("receiver")), refsVal); + stackFrame.to = objectData.value.toString(); + + stackFrame.line = body.value(_("line")).toInt() + 1; + + return stackFrame; +} + +void QmlEnginePrivate::handleFrame(const QVariant &bodyVal, const QVariant &refsVal) +{ + // { "seq" : <number>, + // "type" : "response", + // "request_seq" : <number>, + // "command" : "frame", + // "body" : { "index" : <frame number>, + // "receiver" : <frame receiver>, + // "func" : <function invoked>, + // "script" : <script for the function>, + // "constructCall" : <boolean indicating whether the function was called as constructor>, + // "debuggerFrame" : <boolean indicating whether this is an internal debugger frame>, + // "arguments" : [ { name: <name of the argument - missing of anonymous argument>, + // value: <value of the argument> + // }, + // ... <the array contains all the arguments> + // ], + // "locals" : [ { name: <name of the local variable>, + // value: <value of the local variable> + // }, + // ... <the array contains all the locals> + // ], + // "position" : <source position>, + // "line" : <source line>, + // "column" : <source column within the line>, + // "sourceLineText" : <text for current source line>, + // "scopes" : [ <array of scopes, see scope request below for format> ], + + // } + // "running" : <is the VM running after sending this response> + // "success" : true + // } + QVariantMap currentFrame = bodyVal.toMap(); + + StackHandler *stackHandler = engine->stackHandler(); + WatchHandler * watchHandler = engine->watchHandler(); + watchHandler->notifyUpdateStarted(); + clearCache(); + + const int frameIndex = stackHandler->currentIndex(); + QSet<QByteArray> expandedInames = watchHandler->expandedINames(); + QHash<quint64, QByteArray> handlesToLookup; + // Store handles of all expanded watch data + foreach (const QByteArray &iname, expandedInames) { + const WatchItem *item = watchHandler->findItem(iname); + if (item && item->isLocal()) + handlesToLookup.insert(item->id, iname); + } + if (frameIndex < 0) + return; + const StackFrame frame = stackHandler->currentFrame(); + if (!frame.isUsable()) + return; + + //Set "this" variable + { + auto item = new WatchItem("local.this", QLatin1String("this")); + QmlV8ObjectData objectData = extractData(currentFrame.value(_("receiver")), refsVal); + item->id = objectData.handle; + item->type = objectData.type; + item->value = objectData.value.toString(); + item->setHasChildren(objectData.properties.count()); + // In case of global object, we do not get children + // Set children nevertheless and query later. + if (item->value == QLatin1String("global")) { + item->setHasChildren(true); + item->id = 0; + } + watchHandler->insertItem(item); + } + + const QVariantList scopes = currentFrame.value(_("scopes")).toList(); + foreach (const QVariant &scope, scopes) { + //Do not query for global types (0) + //Showing global properties increases clutter. + if (scope.toMap().value(_("type")).toInt() == 0) + continue; + int scopeIndex = scope.toMap().value(_("index")).toInt(); + currentFrameScopes.append(scopeIndex); + this->scope(scopeIndex); + } + engine->gotoLocation(stackHandler->currentFrame()); + + // Expand watch data that were previously expanded + QHash<quint64, QByteArray>::const_iterator itEnd = handlesToLookup.constEnd(); + for (QHash<quint64, QByteArray>::const_iterator it = handlesToLookup.constBegin(); it != itEnd; ++it) + expandObject(it.value(), it.key()); + engine->stackFrameCompleted(); +} + +void QmlEnginePrivate::handleScope(const QVariant &bodyVal, const QVariant &refsVal) +{ + // { "seq" : <number>, + // "type" : "response", + // "request_seq" : <number>, + // "command" : "scope", + // "body" : { "index" : <index of this scope in the scope chain. Index 0 is the top scope + // and the global scope will always have the highest index for a + // frame>, + // "frameIndex" : <index of the frame>, + // "type" : <type of the scope: + // 0: Global + // 1: Local + // 2: With + // 3: Closure + // 4: Catch >, + // "object" : <the scope object defining the content of the scope. + // For local and closure scopes this is transient objects, + // which has a negative handle value> + // } + // "running" : <is the VM running after sending this response> + // "success" : true + // } + QVariantMap bodyMap = bodyVal.toMap(); + + //Check if the frameIndex is same as current Stack Index + StackHandler *stackHandler = engine->stackHandler(); + if (bodyMap.value(_("frameIndex")).toInt() != stackHandler->currentIndex()) return; - baseTextDocument->document()->setPlainText(textDocument->toPlainText()); + QmlV8ObjectData objectData = extractData(bodyMap.value(_("object")), refsVal); + + QList<int> handlesToLookup; + foreach (const QVariant &property, objectData.properties) { + QmlV8ObjectData localData = extractData(property, refsVal); + auto item = new WatchItem; + item->exp = localData.name; + //Check for v8 specific local data + if (item->exp.startsWith('.') || item->exp.isEmpty()) + continue; + + item->name = QLatin1String(item->exp); + item->iname = QByteArray("local.") + item->exp; + + int handle = localData.handle; + if (localData.value.isValid()) { + item->id = handle; + item->type = localData.type; + item->value = localData.value.toString(); + item->setHasChildren(localData.properties.count()); + engine->watchHandler()->insertItem(item); + } else { + handlesToLookup << handle; + localsAndWatchers.insertMulti(handle, item->exp); + } + } + + if (!handlesToLookup.isEmpty()) + lookup(handlesToLookup); + else + engine->watchHandler()->notifyUpdateFinished(); } -bool QmlEngine::canEvaluateScript(const QString &script) +ConsoleItem *constructLogItemTree(ConsoleItem *parent, + const QmlV8ObjectData &objectData, + const QVariant &refsVal) { - m_interpreter.clearText(); - m_interpreter.appendText(script); - return m_interpreter.canEvaluate(); + bool sorted = boolSetting(SortStructMembers); + if (!objectData.value.isValid()) + return 0; + + QString text; + if (objectData.name.isEmpty()) + text = objectData.value.toString(); + else + text = QString(_("%1: %2")).arg(QString::fromLatin1(objectData.name)) + .arg(objectData.value.toString()); + + ConsoleItem *item = new ConsoleItem(parent, ConsoleItem::UndefinedType, text); + + QSet<QString> childrenFetched; + foreach (const QVariant &property, objectData.properties) { + const QmlV8ObjectData childObjectData = extractData(property, refsVal); + if (childObjectData.handle == objectData.handle) + continue; + ConsoleItem *child = constructLogItemTree(item, childObjectData, refsVal); + if (child) { + const QString text = child->text(); + if (childrenFetched.contains(text)) + continue; + childrenFetched.insert(text); + item->insertChild(child, sorted); + } + } + + return item; } -bool QmlEngine::adjustBreakpointLineAndColumn( - const QString &filePath, quint32 *line, quint32 *column, bool *valid) +static void insertSubItems(WatchItem *parent, const QVariantList &properties, const QVariant &refsVal) { - bool success = false; - //check if file is in the latest snapshot - //ignoring documentChangedOnDisk - //TODO:: update breakpoints if document is changed. - ModelManagerInterface *mmIface = ModelManagerInterface::instance(); - if (mmIface) { - Document::Ptr doc = mmIface->newestSnapshot(). - document(filePath); - if (doc.isNull()) { - ModelManagerInterface::instance()->updateSourceFiles( - QStringList() << filePath, false); + QTC_ASSERT(parent, return); + foreach (const QVariant &property, properties) { + QmlV8ObjectData propertyData = extractData(property, refsVal); + auto item = new WatchItem; + item->name = QString::fromUtf8(propertyData.name); + + // Check for v8 specific local data + if (item->name.startsWith(QLatin1Char('.')) || item->name.isEmpty()) + continue; + if (parent->type == "object") { + if (parent->value == _("Array")) + item->exp = parent->exp + '[' + item->name.toLatin1() + ']'; + else if (parent->value == _("Object")) + item->exp = parent->exp + '.' + item->name.toLatin1(); } else { - ASTWalker walker; - walker(doc->ast(), line, column); - *valid = walker.done; - success = true; + item->exp = item->name.toLatin1(); } + + item->iname = parent->iname + '.' + item->name.toLatin1(); + item->id = propertyData.handle; + item->type = propertyData.type; + item->value = propertyData.value.toString(); + item->setHasChildren(propertyData.properties.count()); + parent->appendChild(item); } - return success; +} + +void QmlEnginePrivate::handleEvaluate(int sequence, bool success, + const QVariant &bodyVal, const QVariant &refsVal) +{ + // { "seq" : <number>, + // "type" : "response", + // "request_seq" : <number>, + // "command" : "evaluate", + // "body" : ... + // "running" : <is the VM running after sending this response> + // "success" : true + // } + WatchHandler *watchHandler = engine->watchHandler(); + if (updateLocalsAndWatchers.contains(sequence)) { + updateLocalsAndWatchers.removeOne(sequence); + //Update the locals + foreach (int index, currentFrameScopes) + scope(index); + //Also update "this" + QByteArray iname("local.this"); + const WatchItem *parent = watchHandler->findItem(iname); + localsAndWatchers.insertMulti(parent->id, iname); + lookup(QList<int>() << parent->id); + + } else if (debuggerCommands.contains(sequence)) { + updateLocalsAndWatchers.removeOne(sequence); + QmlV8ObjectData body = extractData(bodyVal, refsVal); + if (auto consoleManager = ConsoleManagerInterface::instance()) { + if (ConsoleItem *item = constructLogItemTree(consoleManager->rootItem(), body, refsVal)) + consoleManager->printToConsolePane(item); + } + //Update the locals + foreach (int index, currentFrameScopes) + scope(index); + + } else { + QmlV8ObjectData body = extractData(bodyVal, refsVal); + if (evaluatingExpression.contains(sequence)) { + QString exp = evaluatingExpression.take(sequence); + //Do we have request to evaluate a local? + if (exp.startsWith(QLatin1String("local."))) { + WatchItem *item = watchHandler->findItem(exp.toLatin1()); + insertSubItems(item, body.properties, refsVal); + } else { + QByteArray iname = watchHandler->watcherName(exp.toLatin1()); + SDEBUG(QString(iname)); + + auto item = new WatchItem(iname, exp); + item->exp = exp.toLatin1(); + item->id = body.handle; + if (success) { + item->type = body.type; + item->value = body.value.toString(); + item->wantsChildren = body.properties.count(); + } else { + //Do not set type since it is unknown + item->setError(body.value.toString()); + } + watchHandler->insertItem(item); + insertSubItems(item, body.properties, refsVal); + } + //Insert the newly evaluated expression to the Watchers Window + } + } +} + +void QmlEnginePrivate::handleLookup(const QVariant &bodyVal, const QVariant &refsVal) +{ + // { "seq" : <number>, + // "type" : "response", + // "request_seq" : <number>, + // "command" : "lookup", + // "body" : <array of serialized objects indexed using their handle> + // "running" : <is the VM running after sending this response> + // "success" : true + // } + const QVariantMap body = bodyVal.toMap(); + + QStringList handlesList = body.keys(); + WatchHandler *watchHandler = engine->watchHandler(); + foreach (const QString &handle, handlesList) { + QmlV8ObjectData bodyObjectData = extractData(body.value(handle), refsVal); + QByteArray prepend = localsAndWatchers.take(handle.toInt()); + + if (prepend.startsWith("local.") || prepend.startsWith("watch.")) { + // Data for expanded local/watch. + // Could be an object or function. + WatchItem *parent = watchHandler->findItem(prepend); + insertSubItems(parent, bodyObjectData.properties, refsVal); + } else { + //rest + auto item = new WatchItem; + item->exp = prepend; + item->name = QLatin1String(item->exp); + item->iname = QByteArray("local.") + item->exp; + item->id = handle.toInt(); + + item->type = bodyObjectData.type; + item->value = bodyObjectData.value.toString(); + + item->setHasChildren(bodyObjectData.properties.count()); + + engine->watchHandler()->insertItem(item); + } + } + engine->watchHandler()->notifyUpdateFinished(); +} + +void QmlEnginePrivate::stateChanged(State state) +{ + engine->clientStateChanged(state); + + if (state == QmlDebugClient::Enabled) { + /// Start session. + flushSendBuffer(); + connect(); + //Query for the V8 version. This is + //only for logging to the debuggerlog + version(); + } +} + +void QmlEnginePrivate::sendMessage(const QByteArray &msg) +{ + if (state() == Enabled) + QmlDebugClient::sendMessage(msg); + else + sendBuffer.append(msg); +} + +void QmlEnginePrivate::flushSendBuffer() +{ + QTC_ASSERT(state() == Enabled, return); + foreach (const QByteArray &msg, sendBuffer) + QmlDebugClient::sendMessage(msg); + sendBuffer.clear(); } DebuggerEngine *createQmlEngine(const DebuggerRunParameters &sp) @@ -1361,6 +2670,5 @@ DebuggerEngine *createQmlEngine(const DebuggerRunParameters &sp) return new QmlEngine(sp); } -} // namespace Internal -} // namespace Debugger - +} // Internal +} // Debugger diff --git a/src/plugins/debugger/qml/qmlengine.h b/src/plugins/debugger/qml/qmlengine.h index 35517e9c4b..2fe073aa49 100644 --- a/src/plugins/debugger/qml/qmlengine.h +++ b/src/plugins/debugger/qml/qmlengine.h @@ -31,28 +31,21 @@ #ifndef QMLENGINE_H #define QMLENGINE_H -#include "interactiveinterpreter.h" -#include "qmladapter.h" -#include "qmlinspectoradapter.h" #include <debugger/debuggerengine.h> -#include <projectexplorer/applicationlauncher.h> #include <qmldebug/qdebugmessageclient.h> +#include <qmldebug/qmldebugclient.h> #include <qmldebug/qmloutputparser.h> #include <qmljs/iscriptevaluator.h> #include <qmljs/qmljsdocument.h> -QT_FORWARD_DECLARE_CLASS(QTextDocument) - -namespace Core { class IDocument; } - -namespace TextEditor { class BaseTextEditor; } - namespace Debugger { namespace Internal { +class WatchData; +class WatchItem; +class QmlEnginePrivate; class QmlAdapter; -class WatchTreeView; class QmlEngine : public DebuggerEngine, QmlJS::IScriptEvaluator { @@ -63,31 +56,11 @@ public: DebuggerEngine *masterEngine = 0); ~QmlEngine(); - void notifyEngineRemoteServerRunning(const QByteArray &, int pid); - void notifyEngineRemoteSetupFinished(const RemoteSetupResult &result); - - bool canDisplayTooltip() const; - - void showMessage(const QString &msg, int channel = LogDebug, - int timeout = -1) const; - void gotoLocation(const Internal::Location &location); - - void filterApplicationMessage(const QString &msg, int channel); - void inferiorSpontaneousStop(); - - enum LogDirection { - LogSend, - LogReceive - }; - - void logMessage(const QString &service, LogDirection direction, - const QString &str); + void filterApplicationMessage(const QString &msg, int channel) const; - void setSourceFiles(const QStringList &fileNames); - void updateScriptSource(const QString &fileName, int lineOffset, - int columnOffset, const QString &source); - - void insertBreakpoint(Breakpoint bp); + void logServiceStateChange(const QString &service, float version, + QmlDebug::QmlDebugClient::State newState); + void logServiceActivity(const QString &service, const QString &logMessage); private slots: void disconnected(); @@ -96,23 +69,28 @@ private slots: void errorMessageBoxFinished(int result); void updateCurrentContext(); - void appendDebugOutput(QtMsgType type, const QString &message, - const QmlDebug::QDebugContextInfo &info); void tryToConnect(quint16 port = 0); void beginConnection(quint16 port = 0); void connectionEstablished(); void connectionStartupFailed(); void appStartupFailed(const QString &errorMessage); - void connectionError(QDebugSupport::Error error); - void serviceConnectionError(const QString &service); void appendMessage(const QString &msg, Utils::OutputFormat); void synchronizeWatchers(); private: - // DebuggerEngine implementation. + void notifyEngineRemoteServerRunning(const QByteArray &, int pid); + void notifyEngineRemoteSetupFinished(const RemoteSetupResult &result); + + void showMessage(const QString &msg, int channel = LogDebug, + int timeout = -1) const; + void gotoLocation(const Internal::Location &location); + void insertBreakpoint(Breakpoint bp); + bool isSynchronous() const { return false; } + bool canDisplayTooltip() const { return false; } + void executeStep(); void executeStepOut(); void executeNext(); @@ -153,7 +131,6 @@ private: void reloadSourceFiles(); void reloadFullStack() {} - bool supportsThreads() const { return false; } void updateWatchData(const QByteArray &iname); void selectWatchData(const QByteArray &iname); void executeDebuggerCommand(const QString &command, DebuggerLanguages languages); @@ -162,36 +139,21 @@ private: bool hasCapability(unsigned) const; void quitDebugger(); -private: void closeConnection(); void startApplicationLauncher(); void stopApplicationLauncher(); - bool isShadowBuildProject() const; - QString fromShadowBuildFilename(const QString &filename) const; - QString mangleFilenamePaths(const QString &filename, - const QString &oldBasePath, const QString &newBasePath) const; - QString qmlImportPath() const; - - void updateDocument(Core::IDocument *document, const QTextDocument *textDocument); - bool canEvaluateScript(const QString &script); - bool adjustBreakpointLineAndColumn(const QString &filePath, quint32 *line, - quint32 *column, bool *valid); - - QmlAdapter m_adapter; - QmlInspectorAdapter m_inspectorAdapter; - ProjectExplorer::ApplicationLauncher m_applicationLauncher; - QTimer m_noDebugOutputTimer; - QmlDebug::QmlOutputParser m_outputParser; - QHash<QString, QTextDocument*> m_sourceDocuments; - QHash<QString, QWeakPointer<TextEditor::BaseTextEditor> > m_sourceEditors; - InteractiveInterpreter m_interpreter; - QHash<QString,Breakpoint> pendingBreakpoints; - QList<quint32> queryIds; - bool m_retryOnConnectFail; - bool m_automaticConnect; + void connectionErrorOccurred(QDebugSupport::Error socketError); + void clientStateChanged(QmlDebug::QmlDebugClient::State state); + void checkConnectionState(); + void showConnectionStateMessage(const QString &message); + void showConnectionErrorMessage(const QString &message); + bool isConnected() const; +private: friend class QmlCppEngine; + friend class QmlEnginePrivate; + QmlEnginePrivate *d; }; } // namespace Internal diff --git a/src/plugins/debugger/qml/qmlengineutils.cpp b/src/plugins/debugger/qml/qmlengineutils.cpp new file mode 100644 index 0000000000..08670f46e9 --- /dev/null +++ b/src/plugins/debugger/qml/qmlengineutils.cpp @@ -0,0 +1,311 @@ +/**************************************************************************** +** +** 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 "qmlengine.h" + +#include <qmljs/parser/qmljsast_p.h> +#include <qmljs/qmljsmodelmanagerinterface.h> +#include <qmljs/consolemanagerinterface.h> + +#include <coreplugin/editormanager/documentmodel.h> + +#include <texteditor/textdocument.h> +#include <texteditor/texteditor.h> + +#include <QTextBlock> + +using namespace Core; +using namespace QmlDebug; +using namespace QmlJS; +using namespace QmlJS::AST; +using namespace TextEditor; + +namespace Debugger { +namespace Internal { + +class ASTWalker : public Visitor +{ +public: + void operator()(Node *ast, quint32 *l, quint32 *c) + { + done = false; + line = l; + column = c; + Node::accept(ast, this); + } + + bool preVisit(Node *ast) + { + return !done && ast->lastSourceLocation().startLine >= *line; + } + + //Case 1: Breakpoint is between sourceStart(exclusive) and + // sourceEnd(inclusive) --> End tree walk. + //Case 2: Breakpoint is on sourceStart --> Check for the start + // of the first executable code. Set the line number and + // column number. End tree walk. + //Case 3: Breakpoint is on "unbreakable" code --> Find the next "breakable" + // code and check for Case 2. End tree walk. + + //Add more types when suitable. + + bool visit(UiScriptBinding *ast) + { + if (!ast->statement) + return true; + + quint32 sourceStartLine = ast->firstSourceLocation().startLine; + quint32 statementStartLine; + quint32 statementColumn; + + if (ast->statement->kind == Node::Kind_ExpressionStatement) { + statementStartLine = ast->statement->firstSourceLocation().startLine; + statementColumn = ast->statement->firstSourceLocation().startColumn; + + } else if (ast->statement->kind == Node::Kind_Block) { + Block *block = static_cast<Block *>(ast->statement); + if (!block || !block->statements) + return true; + statementStartLine = block->statements->firstSourceLocation().startLine; + statementColumn = block->statements->firstSourceLocation().startColumn; + + } else { + return true; + } + + + //Case 1 + //Check for possible relocation within the binding statement + + //Rewritten to (function <token>() { { }}) + //The offset 16 is position of inner lbrace without token length. + const int offset = 16; + + //Case 2 + if (statementStartLine == *line) { + if (sourceStartLine == *line) + *column = offset + ast->qualifiedId->identifierToken.length; + done = true; + } + + //Case 3 + if (statementStartLine > *line) { + *line = statementStartLine; + if (sourceStartLine == *line) + *column = offset + ast->qualifiedId->identifierToken.length; + else + *column = statementColumn; + done = true; + } + return true; + } + + bool visit(FunctionDeclaration *ast) { + quint32 sourceStartLine = ast->firstSourceLocation().startLine; + quint32 sourceStartColumn = ast->firstSourceLocation().startColumn; + quint32 statementStartLine = ast->body->firstSourceLocation().startLine; + quint32 statementColumn = ast->body->firstSourceLocation().startColumn; + + //Case 1 + //Check for possible relocation within the function declaration + + //Case 2 + if (statementStartLine == *line) { + if (sourceStartLine == *line) + *column = statementColumn - sourceStartColumn + 1; + done = true; + } + + //Case 3 + if (statementStartLine > *line) { + *line = statementStartLine; + if (sourceStartLine == *line) + *column = statementColumn - sourceStartColumn + 1; + else + *column = statementColumn; + done = true; + } + return true; + } + + bool visit(EmptyStatement *ast) + { + *line = ast->lastSourceLocation().startLine + 1; + return true; + } + + bool visit(VariableStatement *ast) { test(ast); return true; } + bool visit(VariableDeclarationList *ast) { test(ast); return true; } + bool visit(VariableDeclaration *ast) { test(ast); return true; } + bool visit(ExpressionStatement *ast) { test(ast); return true; } + bool visit(IfStatement *ast) { test(ast); return true; } + bool visit(DoWhileStatement *ast) { test(ast); return true; } + bool visit(WhileStatement *ast) { test(ast); return true; } + bool visit(ForStatement *ast) { test(ast); return true; } + bool visit(LocalForStatement *ast) { test(ast); return true; } + bool visit(ForEachStatement *ast) { test(ast); return true; } + bool visit(LocalForEachStatement *ast) { test(ast); return true; } + bool visit(ContinueStatement *ast) { test(ast); return true; } + bool visit(BreakStatement *ast) { test(ast); return true; } + bool visit(ReturnStatement *ast) { test(ast); return true; } + bool visit(WithStatement *ast) { test(ast); return true; } + bool visit(SwitchStatement *ast) { test(ast); return true; } + bool visit(CaseBlock *ast) { test(ast); return true; } + bool visit(CaseClauses *ast) { test(ast); return true; } + bool visit(CaseClause *ast) { test(ast); return true; } + bool visit(DefaultClause *ast) { test(ast); return true; } + bool visit(LabelledStatement *ast) { test(ast); return true; } + bool visit(ThrowStatement *ast) { test(ast); return true; } + bool visit(TryStatement *ast) { test(ast); return true; } + bool visit(Catch *ast) { test(ast); return true; } + bool visit(Finally *ast) { test(ast); return true; } + bool visit(FunctionExpression *ast) { test(ast); return true; } + bool visit(DebuggerStatement *ast) { test(ast); return true; } + + void test(Node *ast) + { + quint32 statementStartLine = ast->firstSourceLocation().startLine; + //Case 1/2 + if (statementStartLine <= *line && *line <= ast->lastSourceLocation().startLine) + done = true; + + //Case 3 + if (statementStartLine > *line) { + *line = statementStartLine; + *column = ast->firstSourceLocation().startColumn; + done = true; + } + } + + bool done; + quint32 *line; + quint32 *column; +}; + +bool adjustBreakpointLineAndColumn(const QString &filePath, quint32 *line, quint32 *column, bool *valid) +{ + bool success = false; + //check if file is in the latest snapshot + //ignoring documentChangedOnDisk + //TODO:: update breakpoints if document is changed. + ModelManagerInterface *mmIface = ModelManagerInterface::instance(); + if (mmIface) { + Document::Ptr doc = mmIface->newestSnapshot().document(filePath); + if (doc.isNull()) { + ModelManagerInterface::instance()->updateSourceFiles( + QStringList() << filePath, false); + } else { + ASTWalker walker; + walker(doc->ast(), line, column); + *valid = walker.done; + success = true; + } + } + return success; +} + +void appendDebugOutput(QtMsgType type, const QString &message, const QDebugContextInfo &info) +{ + ConsoleItem::ItemType itemType; + switch (type) { + case QtDebugMsg: + itemType = ConsoleItem::DebugType; + break; + case QtWarningMsg: + itemType = ConsoleItem::WarningType; + break; + case QtCriticalMsg: + case QtFatalMsg: + itemType = ConsoleItem::ErrorType; + break; + default: + //This case is not possible + return; + } + + if (auto consoleManager = ConsoleManagerInterface::instance()) { + ConsoleItem *item = new ConsoleItem(consoleManager->rootItem(), itemType, message); + item->file = info.file; + item->line = info.line; + consoleManager->printToConsolePane(item); + } +} + +void clearExceptionSelection() +{ + QList<QTextEdit::ExtraSelection> selections; + + foreach (IEditor *editor, DocumentModel::editorsForOpenedDocuments()) { + if (auto ed = qobject_cast<TextEditorWidget *>(editor->widget())) + ed->setExtraSelections(TextEditorWidget::DebuggerExceptionSelection, selections); + } +} + +QStringList highlightExceptionCode(int lineNumber, const QString &filePath, const QString &errorMessage) +{ + QStringList messages; + QList<IEditor *> editors = DocumentModel::editorsForFilePath(filePath); + + // set up the format for the errors + QTextCharFormat errorFormat; + errorFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline); + errorFormat.setUnderlineColor(Qt::red); + + foreach (IEditor *editor, editors) { + TextEditorWidget *ed = qobject_cast<TextEditorWidget *>(editor->widget()); + if (!ed) + continue; + + QList<QTextEdit::ExtraSelection> selections; + QTextEdit::ExtraSelection sel; + sel.format = errorFormat; + QTextCursor c(ed->document()->findBlockByNumber(lineNumber - 1)); + const QString text = c.block().text(); + for (int i = 0; i < text.size(); ++i) { + if (!text.at(i).isSpace()) { + c.setPosition(c.position() + i); + break; + } + } + c.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); + sel.cursor = c; + + sel.format.setToolTip(errorMessage); + + selections.append(sel); + ed->setExtraSelections(TextEditorWidget::DebuggerExceptionSelection, selections); + + messages.append(QString::fromLatin1("%1: %2: %3").arg(filePath).arg(lineNumber).arg(errorMessage)); + } + return messages; +} + +} // Internal +} // Debugger diff --git a/tests/manual/qml/testfiles/images.qml b/src/plugins/debugger/qml/qmlengineutils.h index b115ee6540..c3f8c62111 100644 --- a/tests/manual/qml/testfiles/images.qml +++ b/src/plugins/debugger/qml/qmlengineutils.h @@ -28,30 +28,22 @@ ** ****************************************************************************/ -import QtQuick 1.1 +#ifndef QMLENGINEUTILS_H +#define QMLENGINEUTILS_H -Rectangle { - width: 640 - height: 480 +#include <qmldebug/qdebugmessageclient.h> +#include <qmldebug/qmloutputparser.h> - Image { - id: image1 - x: 20 - y: 18 - source: "images/qtcreator.png" - } +namespace Debugger { +namespace Internal { - Image { - id: image2 - x: 327 - y: 18 - source: "images/qtcreator.jpg" - } +bool adjustBreakpointLineAndColumn(const QString &filePath, quint32 *line, quint32 *column, bool *valid); +void appendDebugOutput(QtMsgType type, const QString &message, const QmlDebug::QDebugContextInfo &info); - Image { - id: image3 - x: 20 - y: 288 - source: "images/qtcreator.ico" - } -} +void clearExceptionSelection(); +QStringList highlightExceptionCode(int lineNumber, const QString &filePath, const QString &errorMessage); + +} // namespace Internal +} // namespace Debugger + +#endif // QMLENGINEUTILS_H diff --git a/src/plugins/debugger/qml/qmlinspectoradapter.cpp b/src/plugins/debugger/qml/qmlinspectoradapter.cpp index f98d73959e..43f305b5ff 100644 --- a/src/plugins/debugger/qml/qmlinspectoradapter.cpp +++ b/src/plugins/debugger/qml/qmlinspectoradapter.cpp @@ -30,8 +30,9 @@ #include "qmlinspectoradapter.h" -#include "qmladapter.h" +#include "qmlengine.h" #include "qmlinspectoragent.h" + #include <debugger/debuggeractions.h> #include <debugger/debuggercore.h> #include <debugger/debuggerstringutils.h> @@ -60,15 +61,12 @@ namespace Internal { * QmlInspectorAdapter manages the clients for the inspector, and the * integration with the text editor. */ -QmlInspectorAdapter::QmlInspectorAdapter(QmlAdapter *debugAdapter, - DebuggerEngine *engine, - QObject *parent) - : QObject(parent) - , m_debugAdapter(debugAdapter) - , m_engine(engine) +QmlInspectorAdapter::QmlInspectorAdapter(QmlEngine *engine, QmlDebugConnection *connection) + : m_qmlEngine(engine) + , m_masterEngine(engine) , m_engineClient(0) , m_toolsClient(0) - , m_agent(new QmlInspectorAgent(engine, this)) + , m_agent(new QmlInspectorAgent(engine)) , m_targetToSync(NoTarget) , m_debugIdToSelect(-1) , m_currentSelectedDebugId(-1) @@ -79,16 +77,15 @@ QmlInspectorAdapter::QmlInspectorAdapter(QmlAdapter *debugAdapter, , m_showAppOnTopAction(action(ShowAppOnTop)) , m_engineClientConnected(false) { - if (!m_engine->isMasterEngine()) - m_engine = m_engine->masterEngine(); - connect(m_engine, &DebuggerEngine::stateChanged, + if (!m_masterEngine->isMasterEngine()) + m_masterEngine = m_masterEngine->masterEngine(); + connect(m_masterEngine, &DebuggerEngine::stateChanged, this, &QmlInspectorAdapter::onEngineStateChanged); connect(m_agent, &QmlInspectorAgent::objectFetched, this, &QmlInspectorAdapter::onObjectFetched); connect(m_agent, &QmlInspectorAgent::jumpToObjectDefinition, this, &QmlInspectorAdapter::jumpToObjectDefinitionInEditor); - QmlDebugConnection *connection = m_debugAdapter->connection(); auto engineClient1 = new DeclarativeEngineDebugClient(connection); connect(engineClient1, &BaseEngineDebugClient::newState, this, &QmlInspectorAdapter::clientStateChanged); @@ -186,7 +183,7 @@ void QmlInspectorAdapter::clientStateChanged(QmlDebugClient::State state) version = client->remoteVersion(); } - m_debugAdapter->logServiceStateChange(serviceName, version, state); + m_qmlEngine->logServiceStateChange(serviceName, version, state); } void QmlInspectorAdapter::toolsClientStateChanged(QmlDebugClient::State state) @@ -199,7 +196,7 @@ void QmlInspectorAdapter::toolsClientStateChanged(QmlDebugClient::State state) connect(client, &BaseToolsClient::currentObjectsChanged, this, &QmlInspectorAdapter::selectObjectsFromToolsClient); connect(client, &BaseToolsClient::logActivity, - m_debugAdapter, &QmlAdapter::logServiceActivity); + m_qmlEngine, &QmlEngine::logServiceActivity); connect(client, &BaseToolsClient::reloaded, this, &QmlInspectorAdapter::onReloaded); // register actions here @@ -217,15 +214,15 @@ void QmlInspectorAdapter::toolsClientStateChanged(QmlDebugClient::State state) Core::ICore::addAdditionalContext(m_inspectorToolsContext); m_toolsClientConnected = true; - onEngineStateChanged(m_engine->state()); + onEngineStateChanged(m_masterEngine->state()); if (m_showAppOnTopAction->isChecked()) m_toolsClient->showAppOnTop(true); } else if (m_toolsClientConnected && client == m_toolsClient) { - disconnect(client, SIGNAL(currentObjectsChanged(QList<int>)), - this, SLOT(selectObjectsFromToolsClient(QList<int>))); - disconnect(client, SIGNAL(logActivity(QString,QString)), - m_debugAdapter, SLOT(logServiceActivity(QString,QString))); + disconnect(client, &BaseToolsClient::currentObjectsChanged, + this, &QmlInspectorAdapter::selectObjectsFromToolsClient); + disconnect(client, &BaseToolsClient::logActivity, + m_qmlEngine, &QmlEngine::logServiceActivity); Core::ActionManager::unregisterAction(m_selectAction, Core::Id(Constants::QML_SELECTTOOL)); Core::ActionManager::unregisterAction(m_zoomAction, Core::Id(Constants::QML_ZOOMTOOL)); @@ -316,13 +313,13 @@ void QmlInspectorAdapter::setActiveEngineClient(BaseEngineDebugClient *client) void QmlInspectorAdapter::showConnectionStateMessage(const QString &message) { - m_engine->showMessage(_("QML Inspector: ") + message, LogStatus); + m_masterEngine->showMessage(_("QML Inspector: ") + message, LogStatus); } void QmlInspectorAdapter::jumpToObjectDefinitionInEditor( const FileReference &objSource, int debugId) { - const QString fileName = m_engine->toFileInProject(objSource.url()); + const QString fileName = m_masterEngine->toFileInProject(objSource.url()); Core::EditorManager::openEditorAt(fileName, objSource.lineNumber()); if (debugId != -1 && debugId != m_currentSelectedDebugId) { diff --git a/src/plugins/debugger/qml/qmlinspectoradapter.h b/src/plugins/debugger/qml/qmlinspectoradapter.h index d448a3cc7a..961aa11957 100644 --- a/src/plugins/debugger/qml/qmlinspectoradapter.h +++ b/src/plugins/debugger/qml/qmlinspectoradapter.h @@ -41,13 +41,14 @@ class BaseEngineDebugClient; class BaseToolsClient; class ObjectReference; class FileReference; +class QmlDebugConnection; } namespace Debugger { namespace Internal { class DebuggerEngine; -class QmlAdapter; +class QmlEngine; class QmlInspectorAgent; class QmlInspectorAdapter : public QObject @@ -55,8 +56,7 @@ class QmlInspectorAdapter : public QObject Q_OBJECT public: - QmlInspectorAdapter(QmlAdapter *debugAdapter, DebuggerEngine *engine, - QObject *parent = 0); + QmlInspectorAdapter(QmlEngine *engine, QmlDebug::QmlDebugConnection *connection); ~QmlInspectorAdapter(); QmlDebug::BaseEngineDebugClient *engineClient() const; @@ -96,8 +96,8 @@ private: void enableTools(const bool enable); - QmlAdapter *m_debugAdapter; - DebuggerEngine *m_engine; // Master Engine + QmlEngine *m_qmlEngine; + DebuggerEngine *m_masterEngine; QmlDebug::BaseEngineDebugClient *m_engineClient; QHash<QString, QmlDebug::BaseEngineDebugClient*> m_engineClients; QmlDebug::BaseToolsClient *m_toolsClient; diff --git a/src/plugins/debugger/qml/qmlinspectoragent.cpp b/src/plugins/debugger/qml/qmlinspectoragent.cpp index 2a4dc61478..64bd8cb089 100644 --- a/src/plugins/debugger/qml/qmlinspectoragent.cpp +++ b/src/plugins/debugger/qml/qmlinspectoragent.cpp @@ -686,6 +686,7 @@ void QmlInspectorAgent::insertObjectInTree(const ObjectReference &object) m_objectToSelect = -1; } m_debuggerEngine->watchHandler()->updateWatchersWindow(); + m_debuggerEngine->watchHandler()->reexpandItems(); } void QmlInspectorAgent::buildDebugIdHashRecursive(const ObjectReference &ref) diff --git a/src/plugins/debugger/qml/qmlv8debuggerclient.cpp b/src/plugins/debugger/qml/qmlv8debuggerclient.cpp deleted file mode 100644 index c821da13a0..0000000000 --- a/src/plugins/debugger/qml/qmlv8debuggerclient.cpp +++ /dev/null @@ -1,1799 +0,0 @@ -/**************************************************************************** -** -** 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 "qmlv8debuggerclient.h" -#include "qmlv8debuggerclientconstants.h" -#include "qmlengine.h" - -#include <debugger/debuggerstringutils.h> -#include <debugger/watchhandler.h> -#include <debugger/breakhandler.h> -#include <debugger/stackhandler.h> -#include <debugger/debuggercore.h> -#include <debugger/debuggeractions.h> - -#include <utils/qtcassert.h> -#include <texteditor/texteditor.h> - -#include <coreplugin/editormanager/documentmodel.h> - -#include <qmljs/consolemanagerinterface.h> - -#include <QTextBlock> -#include <QFileInfo> -#include <QJsonArray> -#include <QJsonDocument> -#include <QJsonObject> - -#define DEBUG_QML 0 -#if DEBUG_QML -# define SDEBUG(s) qDebug() << s << '\n' -#else -# define SDEBUG(s) -#endif - -using namespace Core; -using QmlDebug::QmlDebugStream; - -namespace Debugger { -namespace Internal { - -typedef QPair<QByteArray, QByteArray> WatchDataPair; - -struct QmlV8ObjectData { - int handle; - QByteArray name; - QByteArray type; - QVariant value; - QVariantList properties; -}; - -class QmlV8DebuggerClientPrivate -{ -public: - explicit QmlV8DebuggerClientPrivate(QmlV8DebuggerClient *q) : - q(q), - sequence(-1), - engine(0), - previousStepAction(QmlV8DebuggerClient::Continue) - { - } - - void connect(); - void disconnect(); - - void interrupt(); - void continueDebugging(QmlV8DebuggerClient::StepAction stepAction); - - void evaluate(const QString expr, bool global = false, bool disableBreak = false, - int frame = -1, bool addContext = false); - void lookup(const QList<int> handles, bool includeSource = false); - void backtrace(int fromFrame = -1, int toFrame = -1, bool bottom = false); - void frame(int number = -1); - void scope(int number = -1, int frameNumber = -1); - void scripts(int types = 4, const QList<int> ids = QList<int>(), - bool includeSource = false, const QVariant filter = QVariant()); - - void setBreakpoint(const QString type, const QString target, - bool enabled = true,int line = 0, int column = 0, - const QString condition = QString(), int ignoreCount = -1); - void clearBreakpoint(int breakpoint); - void setExceptionBreak(QmlV8DebuggerClient::Exceptions type, bool enabled = false); - - void version(); - //void profile(ProfileCommand command); //NOT SUPPORTED - void clearCache(); - - void sendAndLogV8Request(const QJsonObject &request); - void logSendMessage(const QString &msg) const; - void logReceiveMessage(const QString &msg) const; - -private: - QByteArray packMessage(const QByteArray &type, const QByteArray &message = QByteArray()); - QJsonObject initObject(); - -public: - QmlV8DebuggerClient *q; - - int sequence; - QmlEngine *engine; - QHash<BreakpointModelId, int> breakpoints; - QHash<int, BreakpointModelId> breakpointsSync; - QList<int> breakpointsTemp; - - QHash<int, QString> evaluatingExpression; - QHash<int, QByteArray> localsAndWatchers; - QList<int> updateLocalsAndWatchers; - QList<int> debuggerCommands; - - //Cache - QList<int> currentFrameScopes; - QHash<int, int> stackIndexLookup; - - QmlV8DebuggerClient::StepAction previousStepAction; -}; - -/////////////////////////////////////////////////////////////////////// -// -// QmlV8DebuggerClientPrivate -// -/////////////////////////////////////////////////////////////////////// - -void QmlV8DebuggerClientPrivate::connect() -{ - logSendMessage(QString(_("%1 %2")).arg(_(V8DEBUG), _(CONNECT))); - q->sendMessage(packMessage(CONNECT)); -} - -void QmlV8DebuggerClientPrivate::disconnect() -{ - // { "seq" : <number>, - // "type" : "request", - // "command" : "disconnect", - // } - QJsonObject jsonVal = initObject(); - jsonVal.insert(_(COMMAND), _(DISCONNECT)); - - const QJsonDocument jsonMessage(jsonVal); - logSendMessage(QString::fromLatin1("%1 %2") - .arg(_(V8DEBUG), - QString::fromUtf8(jsonMessage.toJson(QJsonDocument::Compact)))); - q->sendMessage(packMessage(DISCONNECT, jsonMessage.toJson(QJsonDocument::Compact))); -} - -void QmlV8DebuggerClientPrivate::interrupt() -{ - logSendMessage(QString(_("%1 %2")).arg(_(V8DEBUG), _(INTERRUPT))); - q->sendMessage(packMessage(INTERRUPT)); -} - -void QmlV8DebuggerClientPrivate::continueDebugging(QmlV8DebuggerClient::StepAction action) -{ - // { "seq" : <number>, - // "type" : "request", - // "command" : "continue", - // "arguments" : { "stepaction" : <"in", "next" or "out">, - // "stepcount" : <number of steps (default 1)> - // } - // } - QJsonObject jsonVal = initObject(); - jsonVal.insert(_(COMMAND), _(CONTINEDEBUGGING)); - - if (action != QmlV8DebuggerClient::Continue) { - QJsonObject args; - switch (action) { - case QmlV8DebuggerClient::In: - args.insert(_(STEPACTION), _(IN)); - break; - case QmlV8DebuggerClient::Out: - args.insert(_(STEPACTION), _(OUT)); - break; - case QmlV8DebuggerClient::Next: - args.insert(_(STEPACTION), _(NEXT)); - break; - default:break; - } - - jsonVal.insert(_(ARGUMENTS), args); - } - sendAndLogV8Request(jsonVal); - previousStepAction = action; -} - -void QmlV8DebuggerClientPrivate::evaluate(const QString expr, bool global, - bool disableBreak, int frame, - bool addContext) -{ - // { "seq" : <number>, - // "type" : "request", - // "command" : "evaluate", - // "arguments" : { "expression" : <expression to evaluate>, - // "frame" : <number>, - // "global" : <boolean>, - // "disable_break" : <boolean>, - // "additional_context" : [ - // { "name" : <name1>, "handle" : <handle1> }, - // { "name" : <name2>, "handle" : <handle2> }, - // ... - // ] - // } - // } - QJsonObject jsonVal = initObject(); - jsonVal.insert(_(COMMAND), _(EVALUATE)); - - QJsonObject args { - { _(EXPRESSION), expr } - }; - - if (frame != -1) - args.insert(_(FRAME), frame); - - if (global) - args.insert(_(GLOBAL), global); - - if (disableBreak) - args.insert(_(DISABLE_BREAK), disableBreak); - - if (addContext) { - WatchHandler *watchHandler = engine->watchHandler(); - QAbstractItemModel *watchModel = watchHandler->model(); - int rowCount = watchModel->rowCount(); - - QJsonArray ctxtList; - while (rowCount) { - QModelIndex index = watchModel->index(--rowCount, 0); - const WatchData *data = watchHandler->watchItem(index); - const QJsonObject ctxt { - { _(NAME), data->name }, - { _(HANDLE), int(data->id) } - }; - - ctxtList.push_front(ctxt); - } - - args.insert(_(ADDITIONAL_CONTEXT), ctxtList); - } - - jsonVal.insert(_(ARGUMENTS), args); - - sendAndLogV8Request(jsonVal); -} - -void QmlV8DebuggerClientPrivate::lookup(QList<int> handles, bool includeSource) -{ - // { "seq" : <number>, - // "type" : "request", - // "command" : "lookup", - // "arguments" : { "handles" : <array of handles>, - // "includeSource" : <boolean indicating whether - // the source will be included when - // script objects are returned>, - // } - // } - QJsonObject jsonVal = initObject(); - jsonVal.insert(_(COMMAND), _(LOOKUP)); - - QJsonObject args; - - QJsonArray array; - foreach (int handle, handles) - array.push_back(handle); - args.insert(_(HANDLES), array); - - if (includeSource) - args.insert(_(INCLUDESOURCE), includeSource); - - jsonVal.insert(_(ARGUMENTS), args); - - sendAndLogV8Request(jsonVal); -} - -void QmlV8DebuggerClientPrivate::backtrace(int fromFrame, int toFrame, bool bottom) -{ - // { "seq" : <number>, - // "type" : "request", - // "command" : "backtrace", - // "arguments" : { "fromFrame" : <number> - // "toFrame" : <number> - // "bottom" : <boolean, set to true if the bottom of the - // stack is requested> - // } - // } - QJsonObject jsonVal = initObject(); - jsonVal.insert(_(COMMAND), _(BACKTRACE)); - - QJsonObject args; - - if (fromFrame != -1) - args.insert(_(FROMFRAME), fromFrame); - - if (toFrame != -1) - args.insert(_(TOFRAME), toFrame); - - if (bottom) - args.insert(_(BOTTOM), bottom); - - jsonVal.insert(_(ARGUMENTS), args); - - sendAndLogV8Request(jsonVal); -} - -void QmlV8DebuggerClientPrivate::frame(int number) -{ - // { "seq" : <number>, - // "type" : "request", - // "command" : "frame", - // "arguments" : { "number" : <frame number> - // } - // } - QJsonObject jsonVal = initObject(); - jsonVal.insert(_(COMMAND), _(FRAME)); - - if (number != -1) { - const QJsonObject args { - { _(NUMBER), number } - }; - - jsonVal.insert(_(ARGUMENTS), args); - } - - sendAndLogV8Request(jsonVal); -} - -void QmlV8DebuggerClientPrivate::scope(int number, int frameNumber) -{ - // { "seq" : <number>, - // "type" : "request", - // "command" : "scope", - // "arguments" : { "number" : <scope number> - // "frameNumber" : <frame number, optional uses selected - // frame if missing> - // } - // } - QJsonObject jsonVal = initObject(); - jsonVal.insert(_(COMMAND), _(SCOPE)); - - if (number != -1) { - QJsonObject args { - { _(NUMBER), number } - }; - - if (frameNumber != -1) - args.insert(_(FRAMENUMBER), frameNumber); - - jsonVal.insert(_(ARGUMENTS), args); - } - - sendAndLogV8Request(jsonVal); -} - -void QmlV8DebuggerClientPrivate::scripts(int types, const QList<int> ids, bool includeSource, - const QVariant filter) -{ - // { "seq" : <number>, - // "type" : "request", - // "command" : "scripts", - // "arguments" : { "types" : <types of scripts to retrieve - // set bit 0 for native scripts - // set bit 1 for extension scripts - // set bit 2 for normal scripts - // (default is 4 for normal scripts)> - // "ids" : <array of id's of scripts to return. If this is not specified all scripts are requrned> - // "includeSource" : <boolean indicating whether the source code should be included for the scripts returned> - // "filter" : <string or number: filter string or script id. - // If a number is specified, then only the script with the same number as its script id will be retrieved. - // If a string is specified, then only scripts whose names contain the filter string will be retrieved.> - // } - // } - QJsonObject jsonVal = initObject(); - jsonVal.insert(_(COMMAND), _(SCRIPTS)); - - QJsonObject args { - { _(TYPES), types } - }; - - if (ids.count()) { - QJsonArray array; - foreach (int id, ids) { - array.push_back(id); - } - args.insert(_(IDS), array); - } - - if (includeSource) - args.insert(_(INCLUDESOURCE), includeSource); - - QJsonValue filterValue; - if (filter.type() == QVariant::String) - filterValue = filter.toString(); - else if (filter.type() == QVariant::Int) - filterValue = filter.toInt(); - else - QTC_CHECK(!filter.isValid()); - - args.insert(_(FILTER), filterValue); - - jsonVal.insert(_(ARGUMENTS), args); - - sendAndLogV8Request(jsonVal); -} - -void QmlV8DebuggerClientPrivate::setBreakpoint(const QString type, const QString target, - bool enabled, int line, int column, - const QString condition, int ignoreCount) -{ - // { "seq" : <number>, - // "type" : "request", - // "command" : "setbreakpoint", - // "arguments" : { "type" : <"function" or "script" or "scriptId" or "scriptRegExp"> - // "target" : <function expression or script identification> - // "line" : <line in script or function> - // "column" : <character position within the line> - // "enabled" : <initial enabled state. True or false, default is true> - // "condition" : <string with break point condition> - // "ignoreCount" : <number specifying the number of break point hits to ignore, default value is 0> - // } - // } - if (type == _(EVENT)) { - QByteArray params; - QmlDebugStream rs(¶ms, QIODevice::WriteOnly); - rs << target.toUtf8() << enabled; - logSendMessage(QString(_("%1 %2 %3 %4")).arg(_(V8DEBUG), _(BREAKONSIGNAL), target, enabled?_("enabled"):_("disabled"))); - q->sendMessage(packMessage(BREAKONSIGNAL, params)); - - } else { - QJsonObject jsonVal = initObject(); - jsonVal.insert(_(COMMAND), _(SETBREAKPOINT)); - - QJsonObject args { - { _(TYPE), type }, - { _(ENABLED), enabled } - }; - if (type == _(SCRIPTREGEXP)) - args.insert(_(TARGET), - Utils::FileName::fromString(target).fileName()); - else - args.insert(_(TARGET), target); - - if (line) - args.insert(_(LINE), line - 1); - - if (column) - args.insert(_(COLUMN), column - 1); - - if (!condition.isEmpty()) - args.insert(_(CONDITION), condition); - - if (ignoreCount != -1) - args.insert(_(IGNORECOUNT), ignoreCount); - - jsonVal.insert(_(ARGUMENTS), args); - - sendAndLogV8Request(jsonVal); - } -} - -void QmlV8DebuggerClientPrivate::clearBreakpoint(int breakpoint) -{ - // { "seq" : <number>, - // "type" : "request", - // "command" : "clearbreakpoint", - // "arguments" : { "breakpoint" : <number of the break point to clear> - // } - // } - QJsonObject jsonVal = initObject(); - jsonVal.insert(_(COMMAND), _(CLEARBREAKPOINT)); - - QJsonObject args { - { _(BREAKPOINT), breakpoint } - }; - - jsonVal.insert(_(ARGUMENTS), args); - - sendAndLogV8Request(jsonVal); -} - -void QmlV8DebuggerClientPrivate::setExceptionBreak(QmlV8DebuggerClient::Exceptions type, - bool enabled) -{ - // { "seq" : <number>, - // "type" : "request", - // "command" : "setexceptionbreak", - // "arguments" : { "type" : <string: "all", or "uncaught">, - // "enabled" : <optional bool: enables the break type if true> - // } - // } - QJsonObject jsonVal = initObject(); - jsonVal.insert(_(COMMAND), _(SETEXCEPTIONBREAK)); - - QJsonObject args; - - if (type == QmlV8DebuggerClient::AllExceptions) - args.insert(_(TYPE), _(ALL)); - //Not Supported - // else if (type == QmlV8DebuggerClient::UncaughtExceptions) - // args.setProperty(_(TYPE),QScriptValue(_(UNCAUGHT))); - - if (enabled) - args.insert(_(ENABLED), enabled); - - jsonVal.insert(_(ARGUMENTS), args); - - sendAndLogV8Request(jsonVal); -} - -void QmlV8DebuggerClientPrivate::version() -{ - // { "seq" : <number>, - // "type" : "request", - // "command" : "version", - // } - QJsonObject jsonVal = initObject(); - jsonVal.insert(_(COMMAND), _(VERSION)); - - sendAndLogV8Request(jsonVal); -} - -//void QmlV8DebuggerClientPrivate::profile(ProfileCommand command) -//{ -//// { "seq" : <number>, -//// "type" : "request", -//// "command" : "profile", -//// "arguments" : { "command" : "resume" or "pause" } -//// } -// QScriptValue jsonVal = initObject(); -// jsonVal.setProperty(_(COMMAND), QScriptValue(_(PROFILE))); - -// QScriptValue args = m_parser.call(QScriptValue(), QScriptValueList() << QScriptValue(_(OBJECT))); - -// if (command == Resume) -// args.setProperty(_(COMMAND), QScriptValue(_(RESUME))); -// else -// args.setProperty(_(COMMAND), QScriptValue(_(PAUSE))); - -// args.setProperty(_("modules"), QScriptValue(1)); -// jsonVal.setProperty(_(ARGUMENTS), args); - -// const QScriptValue jsonMessage = m_stringifier.call(QScriptValue(), QScriptValueList() << jsonVal); -// logSendMessage(QString(_("%1 %2 %3")).arg(_(V8DEBUG), _(V8REQUEST), jsonMessage.toString())); -// q->sendMessage(packMessage(V8REQUEST, jsonMessage.toString().toUtf8())); -//} - -QVariant valueFromRef(int handle, const QVariant &refsVal, bool *success) -{ - *success = false; - QVariant variant; - const QVariantList refs = refsVal.toList(); - foreach (const QVariant &ref, refs) { - const QVariantMap refData = ref.toMap(); - if (refData.value(_(HANDLE)).toInt() == handle) { - variant = refData; - *success = true; - break; - } - } - return variant; -} - -QmlV8ObjectData extractData(const QVariant &data, const QVariant &refsVal) -{ - // { "handle" : <handle>, - // "type" : <"undefined", "null", "boolean", "number", "string", "object", "function" or "frame"> - // } - - // {"handle":<handle>,"type":"undefined"} - - // {"handle":<handle>,"type":"null"} - - // { "handle":<handle>, - // "type" : <"boolean", "number" or "string"> - // "value" : <JSON encoded value> - // } - - // {"handle":7,"type":"boolean","value":true} - - // {"handle":8,"type":"number","value":42} - - // { "handle" : <handle>, - // "type" : "object", - // "className" : <Class name, ECMA-262 property [[Class]]>, - // "constructorFunction" : {"ref":<handle>}, - // "protoObject" : {"ref":<handle>}, - // "prototypeObject" : {"ref":<handle>}, - // "properties" : [ {"name" : <name>, - // "ref" : <handle> - // }, - // ... - // ] - // } - - // { "handle" : <handle>, - // "type" : "function", - // "className" : "Function", - // "constructorFunction" : {"ref":<handle>}, - // "protoObject" : {"ref":<handle>}, - // "prototypeObject" : {"ref":<handle>}, - // "name" : <function name>, - // "inferredName" : <inferred function name for anonymous functions> - // "source" : <function source>, - // "script" : <reference to function script>, - // "scriptId" : <id of function script>, - // "position" : <function begin position in script>, - // "line" : <function begin source line in script>, - // "column" : <function begin source column in script>, - // "properties" : [ {"name" : <name>, - // "ref" : <handle> - // }, - // ... - // ] - // } - - QmlV8ObjectData objectData; - const QVariantMap dataMap = data.toMap(); - - objectData.name = dataMap.value(_(NAME)).toByteArray(); - - if (dataMap.contains(_(REF))) { - objectData.handle = dataMap.value(_(REF)).toInt(); - bool success; - QVariant dataFromRef = valueFromRef(objectData.handle, refsVal, &success); - if (success) { - QmlV8ObjectData data = extractData(dataFromRef, refsVal); - objectData.type = data.type; - objectData.value = data.value; - objectData.properties = data.properties; - } - } else { - objectData.handle = dataMap.value(_(HANDLE)).toInt(); - QString type = dataMap.value(_(TYPE)).toString(); - - if (type == _("undefined")) { - objectData.type = QByteArray("undefined"); - objectData.value = QVariant(_("undefined")); - - } else if (type == _("null")) { - objectData.type = QByteArray("null"); - objectData.value= QVariant(_("null")); - - } else if (type == _("boolean")) { - objectData.type = QByteArray("boolean"); - objectData.value = dataMap.value(_(VALUE)); - - } else if (type == _("number")) { - objectData.type = QByteArray("number"); - objectData.value = dataMap.value(_(VALUE)); - - } else if (type == _("string")) { - objectData.type = QByteArray("string"); - objectData.value = dataMap.value(_(VALUE)); - - } else if (type == _("object")) { - objectData.type = QByteArray("object"); - objectData.value = dataMap.value(_("className")); - objectData.properties = dataMap.value(_("properties")).toList(); - - } else if (type == _("function")) { - objectData.type = QByteArray("function"); - objectData.value = dataMap.value(_(NAME)); - objectData.properties = dataMap.value(_("properties")).toList(); - - } else if (type == _("script")) { - objectData.type = QByteArray("script"); - objectData.value = dataMap.value(_(NAME)); - } - } - - return objectData; -} - -void QmlV8DebuggerClientPrivate::clearCache() -{ - currentFrameScopes.clear(); - updateLocalsAndWatchers.clear(); -} - -QByteArray QmlV8DebuggerClientPrivate::packMessage(const QByteArray &type, const QByteArray &message) -{ - SDEBUG(message); - QByteArray request; - QmlDebugStream rs(&request, QIODevice::WriteOnly); - QByteArray cmd = V8DEBUG; - rs << cmd << type << message; - return request; -} - -QJsonObject QmlV8DebuggerClientPrivate::initObject() -{ - return QJsonObject { - {_(SEQ), ++sequence}, - {_(TYPE), _(REQUEST)} - }; -} - -void QmlV8DebuggerClientPrivate::sendAndLogV8Request(const QJsonObject &request) -{ - const QJsonDocument jsonMessage(request); - logSendMessage(QString::fromLatin1("%1 %2 %3") - .arg(_(V8DEBUG), _(V8REQUEST), - QString::fromUtf8(jsonMessage.toJson(QJsonDocument::Compact)))); - q->sendMessage(packMessage(V8REQUEST, jsonMessage.toJson(QJsonDocument::Compact))); -} - -void QmlV8DebuggerClientPrivate::logSendMessage(const QString &msg) const -{ - if (engine) - engine->logMessage(QLatin1String("V8DebuggerClient"), QmlEngine::LogSend, msg); -} - -void QmlV8DebuggerClientPrivate::logReceiveMessage(const QString &msg) const -{ - if (engine) - engine->logMessage(QLatin1String("V8DebuggerClient"), QmlEngine::LogReceive, msg); -} - -/////////////////////////////////////////////////////////////////////// -// -// QmlV8DebuggerClient -// -/////////////////////////////////////////////////////////////////////// - -QmlV8DebuggerClient::QmlV8DebuggerClient(QmlDebug::QmlDebugConnection *client) - : BaseQmlDebuggerClient(client, QLatin1String("V8Debugger")), - d(new QmlV8DebuggerClientPrivate(this)) -{ -} - -QmlV8DebuggerClient::~QmlV8DebuggerClient() -{ - delete d; -} - -void QmlV8DebuggerClient::startSession() -{ - flushSendBuffer(); - d->connect(); - //Query for the V8 version. This is - //only for logging to the debuggerlog - d->version(); -} - -void QmlV8DebuggerClient::endSession() -{ - d->disconnect(); -} - -void QmlV8DebuggerClient::resetSession() -{ - clearExceptionSelection(); -} - -void QmlV8DebuggerClient::executeStep() -{ - clearExceptionSelection(); - d->continueDebugging(In); -} - -void QmlV8DebuggerClient::executeStepOut() -{ - clearExceptionSelection(); - d->continueDebugging(Out); -} - -void QmlV8DebuggerClient::executeNext() -{ - clearExceptionSelection(); - d->continueDebugging(Next); -} - -void QmlV8DebuggerClient::executeStepI() -{ - clearExceptionSelection(); - d->continueDebugging(In); -} - -void QmlV8DebuggerClient::executeRunToLine(const ContextData &data) -{ - d->setBreakpoint(QString(_(SCRIPTREGEXP)), data.fileName, - true, data.lineNumber); - clearExceptionSelection(); - d->continueDebugging(Continue); -} - -void QmlV8DebuggerClient::continueInferior() -{ - clearExceptionSelection(); - d->continueDebugging(Continue); -} - -void QmlV8DebuggerClient::interruptInferior() -{ - d->interrupt(); -} - -void QmlV8DebuggerClient::activateFrame(int index) -{ - if (index != d->engine->stackHandler()->currentIndex()) - d->frame(d->stackIndexLookup.value(index)); - d->engine->stackHandler()->setCurrentIndex(index); -} - -bool QmlV8DebuggerClient::acceptsBreakpoint(Breakpoint bp) -{ - BreakpointType type = bp.type(); - return (type == BreakpointOnQmlSignalEmit - || type == BreakpointByFileAndLine - || type == BreakpointAtJavaScriptThrow); -} - -void QmlV8DebuggerClient::insertBreakpoint(Breakpoint bp, - int adjustedLine, - int adjustedColumn) -{ - const BreakpointParameters ¶ms = bp.parameters(); - - if (params.type == BreakpointAtJavaScriptThrow) { - bp.notifyBreakpointInsertOk(); - d->setExceptionBreak(AllExceptions, params.enabled); - - } else if (params.type == BreakpointByFileAndLine) { - d->setBreakpoint(QString(_(SCRIPTREGEXP)), params.fileName, - params.enabled, adjustedLine, adjustedColumn, - QLatin1String(params.condition), params.ignoreCount); - - } else if (params.type == BreakpointOnQmlSignalEmit) { - d->setBreakpoint(QString(_(EVENT)), params.functionName, params.enabled); - bp.notifyBreakpointInsertOk(); - } - - d->breakpointsSync.insert(d->sequence, bp.id()); -} - -void QmlV8DebuggerClient::removeBreakpoint(Breakpoint bp) -{ - const BreakpointParameters ¶ms = bp.parameters(); - - int breakpoint = d->breakpoints.value(bp.id()); - d->breakpoints.remove(bp.id()); - - if (params.type == BreakpointAtJavaScriptThrow) - d->setExceptionBreak(AllExceptions); - else if (params.type == BreakpointOnQmlSignalEmit) - d->setBreakpoint(QString(_(EVENT)), params.functionName, false); - else - d->clearBreakpoint(breakpoint); -} - -void QmlV8DebuggerClient::changeBreakpoint(Breakpoint bp) -{ - const BreakpointParameters ¶ms = bp.parameters(); - - BreakpointResponse br = bp.response(); - if (params.type == BreakpointAtJavaScriptThrow) { - d->setExceptionBreak(AllExceptions, params.enabled); - br.enabled = params.enabled; - bp.setResponse(br); - } else if (params.type == BreakpointOnQmlSignalEmit) { - d->setBreakpoint(QString(_(EVENT)), params.functionName, params.enabled); - br.enabled = params.enabled; - bp.setResponse(br); - } else { - //V8 supports only minimalistic changes in breakpoint - //Remove the breakpoint and add again - bp.notifyBreakpointChangeOk(); - bp.removeBreakpoint(); - BreakHandler *handler = d->engine->breakHandler(); - handler->appendBreakpoint(params); - } -} - -void QmlV8DebuggerClient::synchronizeBreakpoints() -{ - //NOT USED -} - -void QmlV8DebuggerClient::assignValueInDebugger(const WatchData * /*data*/, - const QString &expr, - const QVariant &valueV) -{ - StackHandler *stackHandler = d->engine->stackHandler(); - QString expression = QString(_("%1 = %2;")).arg(expr).arg(valueV.toString()); - if (stackHandler->isContentsValid() && stackHandler->currentFrame().isUsable()) { - d->evaluate(expression, false, false, stackHandler->currentIndex()); - d->updateLocalsAndWatchers.append(d->sequence); - } else { - d->engine->showMessage(QString(_("Cannot evaluate %1 in current stack frame")).arg( - expression), ConsoleOutput); - } -} - -void QmlV8DebuggerClient::updateWatchData(const WatchData &/*data*/) -{ - //NOT USED -} - -void QmlV8DebuggerClient::executeDebuggerCommand(const QString &command) -{ - StackHandler *stackHandler = d->engine->stackHandler(); - if (stackHandler->isContentsValid() && stackHandler->currentFrame().isUsable()) { - d->evaluate(command, false, false, stackHandler->currentIndex()); - d->debuggerCommands.append(d->sequence); - } else { - //Currently cannot evaluate if not in a javascript break - d->engine->showMessage(QString(_("Cannot evaluate %1 in current stack frame")).arg( - command), ConsoleOutput); - } -} - -void QmlV8DebuggerClient::synchronizeWatchers(const QStringList &watchers) -{ - SDEBUG(watchers); - if (d->engine->state() != InferiorStopOk) - return; - - foreach (const QString &exp, watchers) { - StackHandler *stackHandler = d->engine->stackHandler(); - if (stackHandler->isContentsValid() && stackHandler->currentFrame().isUsable()) { - d->evaluate(exp, false, false, stackHandler->currentIndex()); - d->evaluatingExpression.insert(d->sequence, exp); - } - } -} - -void QmlV8DebuggerClient::expandObject(const QByteArray &iname, quint64 objectId) -{ - if (objectId == 0) { - //We may have got the global object - const WatchItem *watch = d->engine->watchHandler()->findItem(iname); - if (watch->value == QLatin1String("global")) { - StackHandler *stackHandler = d->engine->stackHandler(); - if (stackHandler->isContentsValid() && stackHandler->currentFrame().isUsable()) { - d->evaluate(watch->name, false, false, stackHandler->currentIndex()); - d->evaluatingExpression.insert(d->sequence, QLatin1String(iname)); - } - return; - } - } - d->localsAndWatchers.insertMulti(objectId, iname); - d->lookup(QList<int>() << objectId); -} - -void QmlV8DebuggerClient::setEngine(QmlEngine *engine) -{ - d->engine = engine; - connect(this, &QmlV8DebuggerClient::stackFrameCompleted, - engine, &QmlEngine::stackFrameCompleted); -} - -void QmlV8DebuggerClient::getSourceFiles() -{ - d->scripts(4, QList<int>(), true, QVariant()); -} - -void QmlV8DebuggerClient::messageReceived(const QByteArray &data) -{ - QmlDebugStream ds(data); - QByteArray command; - ds >> command; - - if (command == V8DEBUG) { - QByteArray type; - QByteArray response; - ds >> type >> response; - - d->logReceiveMessage(_(V8DEBUG) + QLatin1Char(' ') + QLatin1String(type)); - if (type == CONNECT) { - //debugging session started - - } else if (type == INTERRUPT) { - //debug break requested - - } else if (type == BREAKONSIGNAL) { - //break on signal handler requested - - } else if (type == V8MESSAGE) { - const QString responseString = QLatin1String(response); - SDEBUG(responseString); - d->logReceiveMessage(QLatin1String(V8MESSAGE) + QLatin1Char(' ') + responseString); - - const QVariantMap resp = - QJsonDocument::fromJson(responseString.toUtf8()).toVariant().toMap(); - - const QString type(resp.value(_(TYPE)).toString()); - - if (type == _("response")) { - - bool success = resp.value(_("success")).toBool(); - if (!success) { - SDEBUG("Request was unsuccessful"); - } - - const QString debugCommand(resp.value(_(COMMAND)).toString()); - - if (debugCommand == _(DISCONNECT)) { - //debugging session ended - - } else if (debugCommand == _(CONTINEDEBUGGING)) { - //do nothing, wait for next break - - } else if (debugCommand == _(BACKTRACE)) { - if (success) - updateStack(resp.value(_(BODY)), resp.value(_(REFS))); - - } else if (debugCommand == _(LOOKUP)) { - if (success) - expandLocalsAndWatchers(resp.value(_(BODY)), resp.value(_(REFS))); - - } else if (debugCommand == _(EVALUATE)) { - int seq = resp.value(_("request_seq")).toInt(); - if (success) { - updateEvaluationResult(seq, success, resp.value(_(BODY)), resp.value(_(REFS))); - } else { - QVariantMap map; - map.insert(_(TYPE), QVariant(_("string"))); - map.insert(_(VALUE), resp.value(_("message"))); - updateEvaluationResult(seq, success, QVariant(map), QVariant()); - } - - } else if (debugCommand == _(SETBREAKPOINT)) { - // { "seq" : <number>, - // "type" : "response", - // "request_seq" : <number>, - // "command" : "setbreakpoint", - // "body" : { "type" : <"function" or "script"> - // "breakpoint" : <break point number of the new break point> - // } - // "running" : <is the VM running after sending this response> - // "success" : true - // } - - int seq = resp.value(_("request_seq")).toInt(); - const QVariantMap breakpointData = resp.value(_(BODY)).toMap(); - int index = breakpointData.value(_("breakpoint")).toInt(); - - if (d->breakpointsSync.contains(seq)) { - BreakpointModelId id = d->breakpointsSync.take(seq); - d->breakpoints.insert(id, index); - - //Is actual position info present? Then breakpoint was - //accepted - const QVariantList actualLocations = - breakpointData.value( - _("actual_locations")).toList(); - if (actualLocations.count()) { - //The breakpoint requested line should be same as - //actual line - BreakHandler *handler = d->engine->breakHandler(); - Breakpoint bp = handler->breakpointById(id); - if (bp.state() != BreakpointInserted) { - BreakpointResponse br = bp.response(); - br.lineNumber = breakpointData.value(_("line")).toInt() + 1; - bp.setResponse(br); - bp.notifyBreakpointInsertOk(); - } - } - - - } else { - d->breakpointsTemp.append(index); - } - - - } else if (debugCommand == _(CLEARBREAKPOINT)) { - // DO NOTHING - - } else if (debugCommand == _(SETEXCEPTIONBREAK)) { - // { "seq" : <number>, - // "type" : "response", - // "request_seq" : <number>, - // "command" : "setexceptionbreak", - // "body" : { "type" : <string: "all" or "uncaught" corresponding to the request.>, - // "enabled" : <bool: true if the break type is currently enabled as a result of the request> - // } - // "running" : true - // "success" : true - // } - - - } else if (debugCommand == _(FRAME)) { - if (success) - setCurrentFrameDetails(resp.value(_(BODY)), resp.value(_(REFS))); - - } else if (debugCommand == _(SCOPE)) { - if (success) - updateScope(resp.value(_(BODY)), resp.value(_(REFS))); - - } else if (debugCommand == _(SCRIPTS)) { - // { "seq" : <number>, - // "type" : "response", - // "request_seq" : <number>, - // "command" : "scripts", - // "body" : [ { "name" : <name of the script>, - // "id" : <id of the script> - // "lineOffset" : <line offset within the containing resource> - // "columnOffset" : <column offset within the containing resource> - // "lineCount" : <number of lines in the script> - // "data" : <optional data object added through the API> - // "source" : <source of the script if includeSource was specified in the request> - // "sourceStart" : <first 80 characters of the script if includeSource was not specified in the request> - // "sourceLength" : <total length of the script in characters> - // "scriptType" : <script type (see request for values)> - // "compilationType" : < How was this script compiled: - // 0 if script was compiled through the API - // 1 if script was compiled through eval - // > - // "evalFromScript" : <if "compilationType" is 1 this is the script from where eval was called> - // "evalFromLocation" : { line : < if "compilationType" is 1 this is the line in the script from where eval was called> - // column : < if "compilationType" is 1 this is the column in the script from where eval was called> - // ] - // "running" : <is the VM running after sending this response> - // "success" : true - // } - - if (success) { - const QVariantList body = resp.value(_(BODY)).toList(); - - QStringList sourceFiles; - for (int i = 0; i < body.size(); ++i) { - const QVariantMap entryMap = body.at(i).toMap(); - const int lineOffset = entryMap.value(QLatin1String("lineOffset")).toInt(); - const int columnOffset = entryMap.value(QLatin1String("columnOffset")).toInt(); - const QString name = entryMap.value(QLatin1String("name")).toString(); - const QString source = entryMap.value(QLatin1String("source")).toString(); - - if (name.isEmpty()) - continue; - - if (!sourceFiles.contains(name)) - sourceFiles << name; - - d->engine->updateScriptSource(name, lineOffset, columnOffset, source); - } - d->engine->setSourceFiles(sourceFiles); - } - } else if (debugCommand == _(VERSION)) { - d->logReceiveMessage(QString(_("Using V8 Version: %1")).arg( - resp.value(_(BODY)).toMap(). - value(_("V8Version")).toString())); - - } else { - // DO NOTHING - } - - } else if (type == _(EVENT)) { - const QString eventType(resp.value(_(EVENT)).toString()); - - if (eventType == _("break")) { - const QVariantMap breakData = resp.value(_(BODY)).toMap(); - const QString invocationText = breakData.value(_("invocationText")).toString(); - const QString scriptUrl = breakData.value(_("script")).toMap().value(_("name")).toString(); - const QString sourceLineText = breakData.value(_("sourceLineText")).toString(); - - bool inferiorStop = true; - - QList<int> v8BreakpointIds; - { - const QVariantList v8BreakpointIdList = breakData.value(_("breakpoints")).toList(); - foreach (const QVariant &breakpointId, v8BreakpointIdList) - v8BreakpointIds << breakpointId.toInt(); - } - - if (!v8BreakpointIds.isEmpty() && invocationText.startsWith(_("[anonymous]()")) - && scriptUrl.endsWith(_(".qml")) - && sourceLineText.trimmed().startsWith(QLatin1Char('('))) { - - // we hit most likely the anonymous wrapper function automatically generated for bindings - // -> relocate the breakpoint to column: 1 and continue - - int newColumn = sourceLineText.indexOf(QLatin1Char('(')) + 1; - BreakHandler *handler = d->engine->breakHandler(); - - foreach (int v8Id, v8BreakpointIds) { - const BreakpointModelId id = d->breakpoints.key(v8Id); - Breakpoint bp = handler->breakpointById(id); - if (bp.isValid()) { - const BreakpointParameters ¶ms = bp.parameters(); - - d->clearBreakpoint(v8Id); - d->setBreakpoint(QString(_(SCRIPTREGEXP)), - params.fileName, - params.enabled, - params.lineNumber, - newColumn, - QString(QString::fromLatin1(params.condition)), - params.ignoreCount); - d->breakpointsSync.insert(d->sequence, id); - } - } - d->continueDebugging(Continue); - inferiorStop = false; - } - - //Skip debug break if this is an internal function - if (sourceLineText == _(INTERNAL_FUNCTION)) { - d->continueDebugging(d->previousStepAction); - inferiorStop = false; - } - - if (inferiorStop) { - //Update breakpoint data - BreakHandler *handler = d->engine->breakHandler(); - foreach (int v8Id, v8BreakpointIds) { - const BreakpointModelId id = d->breakpoints.key(v8Id); - Breakpoint bp = handler->breakpointById(id); - if (bp) { - BreakpointResponse br = bp.response(); - if (br.functionName.isEmpty()) { - br.functionName = invocationText; - bp.setResponse(br); - } - if (bp.state() != BreakpointInserted) { - br.lineNumber = breakData.value( - _("sourceLine")).toInt() + 1; - bp.setResponse(br); - bp.notifyBreakpointInsertOk(); - } - } - } - - if (d->engine->state() == InferiorRunOk) { - foreach (const QVariant &breakpointId, v8BreakpointIds) { - if (d->breakpointsTemp.contains(breakpointId.toInt())) - d->clearBreakpoint(breakpointId.toInt()); - } - d->engine->inferiorSpontaneousStop(); - d->backtrace(); - } else if (d->engine->state() == InferiorStopOk) { - d->backtrace(); - } - } - - } else if (eventType == _("exception")) { - const QVariantMap body = resp.value(_(BODY)).toMap(); - int lineNumber = body.value(_("sourceLine")).toInt() + 1; - - const QVariantMap script = body.value(_("script")).toMap(); - QUrl fileUrl(script.value(_(NAME)).toString()); - QString filePath = d->engine->toFileInProject(fileUrl); - - const QVariantMap exception = body.value(_("exception")).toMap(); - QString errorMessage = exception.value(_("text")).toString(); - - highlightExceptionCode(lineNumber, filePath, errorMessage); - - if (d->engine->state() == InferiorRunOk) { - d->engine->inferiorSpontaneousStop(); - d->backtrace(); - } - - if (d->engine->state() == InferiorStopOk) - d->backtrace(); - - } else if (eventType == _("afterCompile")) { - //Currently break point relocation is disabled. - //Uncomment the line below when it will be enabled. -// d->listBreakpoints(); - } - - //Sometimes we do not get event type! - //This is most probably due to a wrong eval expression. - //Redirect output to console. - if (eventType.isEmpty()) { - bool success = resp.value(_("success")).toBool(); - QVariantMap map; - map.insert(_(TYPE), QVariant(_("string"))); - map.insert(_(VALUE), resp.value(_("message"))); - //Since there is no sequence value, best estimate is - //last sequence value - updateEvaluationResult(d->sequence, success, QVariant(map), QVariant()); - } - - } //EVENT - } //V8MESSAGE - - } else { - //DO NOTHING - } -} - -void QmlV8DebuggerClient::updateStack(const QVariant &bodyVal, const QVariant &refsVal) -{ - // { "seq" : <number>, - // "type" : "response", - // "request_seq" : <number>, - // "command" : "backtrace", - // "body" : { "fromFrame" : <number> - // "toFrame" : <number> - // "totalFrames" : <number> - // "frames" : <array of frames - see frame request for details> - // } - // "running" : <is the VM running after sending this response> - // "success" : true - // } - - const QVariantMap body = bodyVal.toMap(); - const QVariantList frames = body.value(_("frames")).toList(); - - int fromFrameIndex = body.value(_("fromFrame")).toInt(); - - QTC_ASSERT(0 == fromFrameIndex, return); - - StackHandler *stackHandler = d->engine->stackHandler(); - StackFrames stackFrames; - int i = 0; - d->stackIndexLookup.clear(); - foreach (const QVariant &frame, frames) { - StackFrame stackFrame = extractStackFrame(frame, refsVal); - if (stackFrame.level < 0) - continue; - d->stackIndexLookup.insert(i, stackFrame.level); - stackFrame.level = i; - stackFrames << stackFrame; - i++; - } - stackHandler->setFrames(stackFrames); - - //Populate locals and watchers wrt top frame - //Update all Locals visible in current scope - //Traverse the scope chain and store the local properties - //in a list and show them in the Locals Window. - setCurrentFrameDetails(frames.value(0), refsVal); -} - -StackFrame QmlV8DebuggerClient::extractStackFrame(const QVariant &bodyVal, const QVariant &refsVal) -{ - // { "seq" : <number>, - // "type" : "response", - // "request_seq" : <number>, - // "command" : "frame", - // "body" : { "index" : <frame number>, - // "receiver" : <frame receiver>, - // "func" : <function invoked>, - // "script" : <script for the function>, - // "constructCall" : <boolean indicating whether the function was called as constructor>, - // "debuggerFrame" : <boolean indicating whether this is an internal debugger frame>, - // "arguments" : [ { name: <name of the argument - missing of anonymous argument>, - // value: <value of the argument> - // }, - // ... <the array contains all the arguments> - // ], - // "locals" : [ { name: <name of the local variable>, - // value: <value of the local variable> - // }, - // ... <the array contains all the locals> - // ], - // "position" : <source position>, - // "line" : <source line>, - // "column" : <source column within the line>, - // "sourceLineText" : <text for current source line>, - // "scopes" : [ <array of scopes, see scope request below for format> ], - - // } - // "running" : <is the VM running after sending this response> - // "success" : true - // } - - const QVariantMap body = bodyVal.toMap(); - - StackFrame stackFrame; - stackFrame.level = body.value(_("index")).toInt(); - //Do not insert the frame corresponding to the internal function - if (body.value(QLatin1String("sourceLineText")) == QLatin1String(INTERNAL_FUNCTION)) { - stackFrame.level = -1; - return stackFrame; - } - - QmlV8ObjectData objectData = extractData(body.value(_("func")), refsVal); - QString functionName = objectData.value.toString(); - if (functionName.isEmpty()) - functionName = tr("Anonymous Function"); - stackFrame.function = functionName; - - objectData = extractData(body.value(_("script")), refsVal); - stackFrame.file = d->engine->toFileInProject(objectData.value.toString()); - stackFrame.usable = QFileInfo(stackFrame.file).isReadable(); - - objectData = extractData(body.value(_("receiver")), refsVal); - stackFrame.to = objectData.value.toString(); - - stackFrame.line = body.value(_("line")).toInt() + 1; - - return stackFrame; -} - -void QmlV8DebuggerClient::setCurrentFrameDetails(const QVariant &bodyVal, const QVariant &refsVal) -{ - // { "seq" : <number>, - // "type" : "response", - // "request_seq" : <number>, - // "command" : "frame", - // "body" : { "index" : <frame number>, - // "receiver" : <frame receiver>, - // "func" : <function invoked>, - // "script" : <script for the function>, - // "constructCall" : <boolean indicating whether the function was called as constructor>, - // "debuggerFrame" : <boolean indicating whether this is an internal debugger frame>, - // "arguments" : [ { name: <name of the argument - missing of anonymous argument>, - // value: <value of the argument> - // }, - // ... <the array contains all the arguments> - // ], - // "locals" : [ { name: <name of the local variable>, - // value: <value of the local variable> - // }, - // ... <the array contains all the locals> - // ], - // "position" : <source position>, - // "line" : <source line>, - // "column" : <source column within the line>, - // "sourceLineText" : <text for current source line>, - // "scopes" : [ <array of scopes, see scope request below for format> ], - - // } - // "running" : <is the VM running after sending this response> - // "success" : true - // } - QVariantMap currentFrame = bodyVal.toMap(); - - StackHandler *stackHandler = d->engine->stackHandler(); - WatchHandler * watchHandler = d->engine->watchHandler(); - watchHandler->notifyUpdateStarted(); - d->clearCache(); - - const int frameIndex = stackHandler->currentIndex(); - QSet<QByteArray> expandedInames = watchHandler->expandedINames(); - QHash<quint64, QByteArray> handlesToLookup; - // Store handles of all expanded watch data - foreach (const QByteArray &iname, expandedInames) { - const WatchItem *item = watchHandler->findItem(iname); - if (item && item->isLocal()) - handlesToLookup.insert(item->id, iname); - } - if (frameIndex < 0) - return; - const StackFrame frame = stackHandler->currentFrame(); - if (!frame.isUsable()) - return; - - //Set "this" variable - { - auto item = new WatchItem("local.this", QLatin1String("this")); - QmlV8ObjectData objectData = extractData(currentFrame.value(_("receiver")), refsVal); - item->id = objectData.handle; - item->type = objectData.type; - item->value = objectData.value.toString(); - item->setHasChildren(objectData.properties.count()); - //Incase of global object, we do not get children - //Set children nevertheless and query later - if (item->value == QLatin1String("global")) { - item->setHasChildren(true); - item->id = 0; - } - watchHandler->insertItem(item); - } - - const QVariantList currentFrameScopes = currentFrame.value(_("scopes")).toList(); - foreach (const QVariant &scope, currentFrameScopes) { - //Do not query for global types (0) - //Showing global properties increases clutter. - if (scope.toMap().value(_("type")).toInt() == 0) - continue; - int scopeIndex = scope.toMap().value(_("index")).toInt(); - d->currentFrameScopes.append(scopeIndex); - d->scope(scopeIndex); - } - d->engine->gotoLocation(stackHandler->currentFrame()); - - // Expand watch data that were previously expanded - QHash<quint64, QByteArray>::const_iterator itEnd = handlesToLookup.constEnd(); - for (QHash<quint64, QByteArray>::const_iterator it = handlesToLookup.constBegin(); it != itEnd; ++it) - expandObject(it.value(), it.key()); - emit stackFrameCompleted(); -} - -void QmlV8DebuggerClient::updateScope(const QVariant &bodyVal, const QVariant &refsVal) -{ -// { "seq" : <number>, -// "type" : "response", -// "request_seq" : <number>, -// "command" : "scope", -// "body" : { "index" : <index of this scope in the scope chain. Index 0 is the top scope -// and the global scope will always have the highest index for a -// frame>, -// "frameIndex" : <index of the frame>, -// "type" : <type of the scope: -// 0: Global -// 1: Local -// 2: With -// 3: Closure -// 4: Catch >, -// "object" : <the scope object defining the content of the scope. -// For local and closure scopes this is transient objects, -// which has a negative handle value> -// } -// "running" : <is the VM running after sending this response> -// "success" : true -// } - QVariantMap bodyMap = bodyVal.toMap(); - - //Check if the frameIndex is same as current Stack Index - StackHandler *stackHandler = d->engine->stackHandler(); - if (bodyMap.value(_("frameIndex")).toInt() != stackHandler->currentIndex()) - return; - - QmlV8ObjectData objectData = extractData(bodyMap.value(_("object")), refsVal); - - QList<int> handlesToLookup; - foreach (const QVariant &property, objectData.properties) { - QmlV8ObjectData localData = extractData(property, refsVal); - auto item = new WatchItem; - item->exp = localData.name; - //Check for v8 specific local data - if (item->exp.startsWith('.') || item->exp.isEmpty()) - continue; - - item->name = QLatin1String(item->exp); - item->iname = QByteArray("local.") + item->exp; - - int handle = localData.handle; - if (localData.value.isValid()) { - item->id = handle; - item->type = localData.type; - item->value = localData.value.toString(); - item->setHasChildren(localData.properties.count()); - d->engine->watchHandler()->insertItem(item); - } else { - handlesToLookup << handle; - d->localsAndWatchers.insertMulti(handle, item->exp); - } - } - - if (!handlesToLookup.isEmpty()) - d->lookup(handlesToLookup); - else - d->engine->watchHandler()->notifyUpdateFinished(); -} - -QmlJS::ConsoleItem *constructLogItemTree(QmlJS::ConsoleItem *parent, - const QmlV8ObjectData &objectData, - const QVariant &refsVal) -{ - using namespace QmlJS; - bool sorted = boolSetting(SortStructMembers); - if (!objectData.value.isValid()) - return 0; - - QString text; - if (objectData.name.isEmpty()) - text = objectData.value.toString(); - else - text = QString(_("%1: %2")).arg(QString::fromLatin1(objectData.name)) - .arg(objectData.value.toString()); - - ConsoleItem *item = new ConsoleItem(parent, ConsoleItem::UndefinedType, text); - - QSet<QString> childrenFetched; - foreach (const QVariant &property, objectData.properties) { - const QmlV8ObjectData childObjectData = extractData(property, refsVal); - if (childObjectData.handle == objectData.handle) - continue; - ConsoleItem *child = constructLogItemTree(item, childObjectData, refsVal); - if (child) { - const QString text = child->text(); - if (childrenFetched.contains(text)) - continue; - childrenFetched.insert(text); - item->insertChild(child, sorted); - } - } - - return item; -} - -void QmlV8DebuggerClient::updateEvaluationResult(int sequence, bool success, - const QVariant &bodyVal, const QVariant &refsVal) -{ - // { "seq" : <number>, - // "type" : "response", - // "request_seq" : <number>, - // "command" : "evaluate", - // "body" : ... - // "running" : <is the VM running after sending this response> - // "success" : true - // } - WatchHandler *watchHandler = d->engine->watchHandler(); - if (d->updateLocalsAndWatchers.contains(sequence)) { - d->updateLocalsAndWatchers.removeOne(sequence); - //Update the locals - foreach (int index, d->currentFrameScopes) - d->scope(index); - //Also update "this" - QByteArray iname("local.this"); - const WatchItem *parent = watchHandler->findItem(iname); - d->localsAndWatchers.insertMulti(parent->id, iname); - d->lookup(QList<int>() << parent->id); - - } else if (d->debuggerCommands.contains(sequence)) { - d->updateLocalsAndWatchers.removeOne(sequence); - QmlV8ObjectData body = extractData(bodyVal, refsVal); - using namespace QmlJS; - ConsoleManagerInterface *consoleManager = ConsoleManagerInterface::instance(); - if (consoleManager) { - ConsoleItem *item = constructLogItemTree(consoleManager->rootItem(), body, refsVal); - if (item) - consoleManager->printToConsolePane(item); - } - //Update the locals - foreach (int index, d->currentFrameScopes) - d->scope(index); - - } else { - QmlV8ObjectData body = extractData(bodyVal, refsVal); - if (d->evaluatingExpression.contains(sequence)) { - QString exp = d->evaluatingExpression.take(sequence); - //Do we have request to evaluate a local? - if (exp.startsWith(QLatin1String("local."))) { - const WatchItem *item = watchHandler->findItem(exp.toLatin1()); - createWatchDataList(item, body.properties, refsVal); - } else { - QByteArray iname = watchHandler->watcherName(exp.toLatin1()); - SDEBUG(QString(iname)); - - auto item = new WatchItem(iname, exp); - item->exp = exp.toLatin1(); - item->id = body.handle; - if (success) { - item->type = body.type; - item->value = body.value.toString(); - item->wantsChildren = body.properties.count(); - } else { - //Do not set type since it is unknown - item->setError(body.value.toString()); - } - watchHandler->insertItem(item); - createWatchDataList(item, body.properties, refsVal); - } - //Insert the newly evaluated expression to the Watchers Window - } - } -} - -void QmlV8DebuggerClient::expandLocalsAndWatchers(const QVariant &bodyVal, const QVariant &refsVal) -{ - // { "seq" : <number>, - // "type" : "response", - // "request_seq" : <number>, - // "command" : "lookup", - // "body" : <array of serialized objects indexed using their handle> - // "running" : <is the VM running after sending this response> - // "success" : true - // } - const QVariantMap body = bodyVal.toMap(); - - QStringList handlesList = body.keys(); - WatchHandler *watchHandler = d->engine->watchHandler(); - foreach (const QString &handle, handlesList) { - QmlV8ObjectData bodyObjectData = extractData( - body.value(handle), refsVal); - QByteArray prepend = d->localsAndWatchers.take(handle.toInt()); - - if (prepend.startsWith("local.") || prepend.startsWith("watch.")) { - // Data for expanded local/watch. - // Could be an object or function. - const WatchItem *parent = watchHandler->findItem(prepend); - createWatchDataList(parent, bodyObjectData.properties, refsVal); - } else { - //rest - auto item = new WatchItem; - item->exp = prepend; - item->name = QLatin1String(item->exp); - item->iname = QByteArray("local.") + item->exp; - item->id = handle.toInt(); - - item->type = bodyObjectData.type; - item->value = bodyObjectData.value.toString(); - - item->setHasChildren(bodyObjectData.properties.count()); - - d->engine->watchHandler()->insertItem(item); - } - } - d->engine->watchHandler()->notifyUpdateFinished(); -} - -void QmlV8DebuggerClient::createWatchDataList(const WatchItem *parent, - const QVariantList &properties, - const QVariant &refsVal) -{ - if (properties.count()) { - QTC_ASSERT(parent, return); - foreach (const QVariant &property, properties) { - QmlV8ObjectData propertyData = extractData(property, refsVal); - auto item = new WatchItem; - item->name = QString::fromUtf8(propertyData.name); - - //Check for v8 specific local data - if (item->name.startsWith(QLatin1Char('.')) || item->name.isEmpty()) - continue; - if (parent->type == "object") { - if (parent->value == _("Array")) - item->exp = parent->exp + '[' + item->name.toLatin1() + ']'; - else if (parent->value == _("Object")) - item->exp = parent->exp + '.' + item->name.toLatin1(); - } else { - item->exp = item->name.toLatin1(); - } - - item->iname = parent->iname + '.' + item->name.toLatin1(); - item->id = propertyData.handle; - item->type = propertyData.type; - item->value = propertyData.value.toString(); - item->setHasChildren(propertyData.properties.count()); - d->engine->watchHandler()->insertItem(item); - } - } -} - -void QmlV8DebuggerClient::highlightExceptionCode(int lineNumber, - const QString &filePath, - const QString &errorMessage) -{ - QList<IEditor *> editors = DocumentModel::editorsForFilePath(filePath); - - // set up the format for the errors - QTextCharFormat errorFormat; - errorFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline); - errorFormat.setUnderlineColor(Qt::red); - - foreach (IEditor *editor, editors) { - TextEditor::TextEditorWidget *ed = qobject_cast<TextEditor::TextEditorWidget *>(editor->widget()); - if (!ed) - continue; - - QList<QTextEdit::ExtraSelection> selections; - QTextEdit::ExtraSelection sel; - sel.format = errorFormat; - QTextCursor c(ed->document()->findBlockByNumber(lineNumber - 1)); - const QString text = c.block().text(); - for (int i = 0; i < text.size(); ++i) { - if (! text.at(i).isSpace()) { - c.setPosition(c.position() + i); - break; - } - } - c.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); - sel.cursor = c; - - sel.format.setToolTip(errorMessage); - - selections.append(sel); - ed->setExtraSelections(TextEditor::TextEditorWidget::DebuggerExceptionSelection, selections); - - QString message = QString(_("%1: %2: %3")).arg(filePath).arg(lineNumber) - .arg(errorMessage); - d->engine->showMessage(message, ConsoleOutput); - } -} - -void QmlV8DebuggerClient::clearExceptionSelection() -{ - QList<QTextEdit::ExtraSelection> selections; - - foreach (IEditor *editor, DocumentModel::editorsForOpenedDocuments()) { - TextEditor::TextEditorWidget *ed = qobject_cast<TextEditor::TextEditorWidget *>(editor->widget()); - if (!ed) - continue; - - ed->setExtraSelections(TextEditor::TextEditorWidget::DebuggerExceptionSelection, selections); - } - -} - -} // Internal -} // Debugger diff --git a/src/plugins/debugger/qml/qmlv8debuggerclient.h b/src/plugins/debugger/qml/qmlv8debuggerclient.h deleted file mode 100644 index ae78443e77..0000000000 --- a/src/plugins/debugger/qml/qmlv8debuggerclient.h +++ /dev/null @@ -1,130 +0,0 @@ -/**************************************************************************** -** -** 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 QMLV8DEBUGGERCLIENT_H -#define QMLV8DEBUGGERCLIENT_H - -#include "baseqmldebuggerclient.h" - -namespace Debugger { -namespace Internal { - -class QmlV8DebuggerClientPrivate; - -class QmlV8DebuggerClient : public BaseQmlDebuggerClient -{ - Q_OBJECT - - enum Exceptions - { - NoExceptions, - UncaughtExceptions, - AllExceptions - }; - - enum StepAction - { - Continue, - In, - Out, - Next - }; - -public: - explicit QmlV8DebuggerClient(QmlDebug::QmlDebugConnection *client); - ~QmlV8DebuggerClient(); - - void startSession(); - void endSession(); - void resetSession(); - - void executeStep(); - void executeStepOut(); - void executeNext(); - void executeStepI(); - - void executeRunToLine(const ContextData &data); - - void continueInferior(); - void interruptInferior(); - - void activateFrame(int index); - - bool acceptsBreakpoint(Breakpoint bp); - void insertBreakpoint(Breakpoint bp, int adjustedLine, - int adjustedColumn = -1); - void removeBreakpoint(Breakpoint bp); - void changeBreakpoint(Breakpoint bp); - void synchronizeBreakpoints(); - - void assignValueInDebugger(const WatchData *data, - const QString &expression, - const QVariant &valueV); - - void updateWatchData(const WatchData &); - void executeDebuggerCommand(const QString &command); - - void synchronizeWatchers(const QStringList &watchers); - - void expandObject(const QByteArray &iname, quint64 objectId); - - void setEngine(QmlEngine *engine); - - void getSourceFiles(); - -protected: - void messageReceived(const QByteArray &data); - -private: - void updateStack(const QVariant &bodyVal, const QVariant &refsVal); - StackFrame extractStackFrame(const QVariant &bodyVal, const QVariant &refsVal); - void setCurrentFrameDetails(const QVariant &bodyVal, const QVariant &refsVal); - void updateScope(const QVariant &bodyVal, const QVariant &refsVal); - - void updateEvaluationResult(int sequence, bool success, const QVariant &bodyVal, - const QVariant &refsVal); - void expandLocalsAndWatchers(const QVariant &bodyVal, const QVariant &refsVal); - void createWatchDataList(const WatchItem *parent, - const QVariantList &properties, - const QVariant &refsVal); - - void highlightExceptionCode(int lineNumber, const QString &filePath, - const QString &errorMessage); - void clearExceptionSelection(); - -private: - QmlV8DebuggerClientPrivate *d; - friend class QmlV8DebuggerClientPrivate; -}; - -} // Internal -} // Debugger - -#endif // QMLV8DEBUGGERCLIENT_H diff --git a/src/plugins/debugger/qml/qscriptdebuggerclient.cpp b/src/plugins/debugger/qml/qscriptdebuggerclient.cpp deleted file mode 100644 index fd9b246557..0000000000 --- a/src/plugins/debugger/qml/qscriptdebuggerclient.cpp +++ /dev/null @@ -1,606 +0,0 @@ -/**************************************************************************** -** -** 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 "qscriptdebuggerclient.h" - -#include "qmlengine.h" -#include <debugger/watchhandler.h> -#include <debugger/breakhandler.h> -#include <debugger/stackhandler.h> -#include <debugger/debuggercore.h> -#include <debugger/debuggerstringutils.h> -#include <qmldebug/qmldebugclient.h> - -#include <coreplugin/messagebox.h> - -#include <QFileInfo> -#include <utils/qtcassert.h> - -using QmlDebug::QmlDebugStream; - -namespace Debugger { -namespace Internal { - -struct JSAgentBreakpointData -{ - QByteArray functionName; - QByteArray fileUrl; - qint32 lineNumber; -}; - -struct JSAgentStackData -{ - QByteArray functionName; - QByteArray fileUrl; - qint32 lineNumber; -}; - -uint qHash(const JSAgentBreakpointData &b) -{ - return b.lineNumber ^ qHash(b.fileUrl); -} - -QDataStream &operator<<(QDataStream &s, const JSAgentBreakpointData &data) -{ - return s << data.functionName << data.fileUrl << data.lineNumber; -} - -QDataStream &operator<<(QDataStream &s, const JSAgentStackData &data) -{ - return s << data.functionName << data.fileUrl << data.lineNumber; -} - -QDataStream &operator>>(QDataStream &s, JSAgentBreakpointData &data) -{ - return s >> data.functionName >> data.fileUrl >> data.lineNumber; -} - -QDataStream &operator>>(QDataStream &s, JSAgentStackData &data) -{ - return s >> data.functionName >> data.fileUrl >> data.lineNumber; -} - -bool operator==(const JSAgentBreakpointData &b1, const JSAgentBreakpointData &b2) -{ - return b1.lineNumber == b2.lineNumber && b1.fileUrl == b2.fileUrl; -} - -typedef QSet<JSAgentBreakpointData> JSAgentBreakpoints; -typedef QList<JSAgentStackData> JSAgentStackFrames; - - -static QDataStream &operator>>(QDataStream &s, WatchData &data) -{ - data = WatchData(); - QByteArray name; - QByteArray value; - QByteArray type; - bool hasChildren = false; - s >> data.exp >> name >> value >> type >> hasChildren >> data.id; - data.name = QString::fromUtf8(name); - data.setType(type, false); - data.setValue(QString::fromUtf8(value)); - data.setHasChildren(hasChildren); - data.setAllUnneeded(); - return s; -} - -class QScriptDebuggerClientPrivate -{ -public: - explicit QScriptDebuggerClientPrivate(QScriptDebuggerClient *) : - ping(0), sessionStarted(false), engine(0) - { - - } - - int ping; - bool sessionStarted; - QmlEngine *engine; - JSAgentBreakpoints breakpoints; - - void logSendMessage(const QString &msg) const; - void logReceiveMessage(const QString &msg) const; -}; - -QScriptDebuggerClient::QScriptDebuggerClient(QmlDebug::QmlDebugConnection* client) - : BaseQmlDebuggerClient(client, QLatin1String("JSDebugger")), - d(new QScriptDebuggerClientPrivate(this)) -{ -} - -QScriptDebuggerClient::~QScriptDebuggerClient() -{ - delete d; -} - -void QScriptDebuggerClient::executeStep() -{ - QByteArray reply; - QmlDebugStream rs(&reply, QIODevice::WriteOnly); - QByteArray cmd = "STEPINTO"; - rs << cmd; - d->logSendMessage(QLatin1String(cmd)); - sendMessage(reply); -} - -void QScriptDebuggerClient::executeStepOut() -{ - QByteArray reply; - QmlDebugStream rs(&reply, QIODevice::WriteOnly); - QByteArray cmd = "STEPOUT"; - rs << cmd; - d->logSendMessage(QLatin1String(cmd)); - sendMessage(reply); -} - -void QScriptDebuggerClient::executeNext() -{ - QByteArray reply; - QmlDebugStream rs(&reply, QIODevice::WriteOnly); - QByteArray cmd = "STEPOVER"; - rs << cmd; - d->logSendMessage(QLatin1String(cmd)); - sendMessage(reply); -} - -void QScriptDebuggerClient::executeStepI() -{ - QByteArray reply; - QmlDebugStream rs(&reply, QIODevice::WriteOnly); - QByteArray cmd = "STEPINTO"; - rs << cmd; - d->logSendMessage(QLatin1String(cmd)); - sendMessage(reply); -} - -void QScriptDebuggerClient::executeRunToLine(const ContextData &data) -{ - JSAgentBreakpointData bp; - bp.fileUrl = QUrl::fromLocalFile(data.fileName).toString().toUtf8(); - bp.lineNumber = data.lineNumber; - bp.functionName = "TEMPORARY"; - d->breakpoints.insert(bp); - synchronizeBreakpoints(); - continueInferior(); -} - -void QScriptDebuggerClient::continueInferior() -{ - QByteArray reply; - QmlDebugStream rs(&reply, QIODevice::WriteOnly); - QByteArray cmd = "CONTINUE"; - rs << cmd; - d->logSendMessage(QLatin1String(cmd)); - sendMessage(reply); -} - -void QScriptDebuggerClient::interruptInferior() -{ - QByteArray reply; - QmlDebugStream rs(&reply, QIODevice::WriteOnly); - QByteArray cmd = "INTERRUPT"; - rs << cmd; - d->logSendMessage(QLatin1String(cmd)); - sendMessage(reply); -} - -void QScriptDebuggerClient::startSession() -{ - //Flush buffered data - flushSendBuffer(); - - //Set all breakpoints - BreakHandler *handler = d->engine->breakHandler(); - DebuggerEngine * engine = d->engine->isSlaveEngine() ? - d->engine->masterEngine() : d->engine; - foreach (Breakpoint bp, handler->engineBreakpoints(engine)) { - QTC_CHECK(bp.state() == BreakpointInsertProceeding); - bp.notifyBreakpointInsertOk(); - } - d->sessionStarted = true; -} - -void QScriptDebuggerClient::endSession() -{ -} - -void QScriptDebuggerClient::resetSession() -{ - d->sessionStarted = false; -} - -void QScriptDebuggerClient::activateFrame(int index) -{ - QByteArray reply; - QmlDebugStream rs(&reply, QIODevice::WriteOnly); - QByteArray cmd = "ACTIVATE_FRAME"; - rs << cmd - << index; - d->logSendMessage(QLatin1String(cmd) + QLatin1Char(' ') + QString::number(index)); - sendMessage(reply); -} - -void QScriptDebuggerClient::insertBreakpoint(Breakpoint bp, - int adjustedLine, - int /*adjustedColumn*/) -{ - JSAgentBreakpointData jsbp; - jsbp.fileUrl = QUrl::fromLocalFile(bp.fileName()).toString().toUtf8(); - jsbp.lineNumber = adjustedLine; - jsbp.functionName = bp.functionName().toUtf8(); - d->breakpoints.insert(jsbp); - - BreakpointResponse br = bp.response(); - br.lineNumber = adjustedLine; - bp.setResponse(br); - if (d->sessionStarted && bp.state() == BreakpointInsertProceeding) - bp.notifyBreakpointInsertOk(); -} - -void QScriptDebuggerClient::removeBreakpoint(Breakpoint bp) -{ - JSAgentBreakpointData jsbp; - jsbp.fileUrl = QUrl::fromLocalFile(bp.fileName()).toString().toUtf8(); - jsbp.lineNumber = bp.lineNumber(); - jsbp.functionName = bp.functionName().toUtf8(); - d->breakpoints.remove(jsbp); -} - -void QScriptDebuggerClient::changeBreakpoint(Breakpoint bp) -{ - if (bp.isEnabled()) - insertBreakpoint(bp, bp.response().lineNumber); - else - removeBreakpoint(bp); - - BreakpointResponse br = bp.response(); - br.enabled = bp.isEnabled(); - bp.setResponse(br); -} - -void QScriptDebuggerClient::synchronizeBreakpoints() -{ - QByteArray reply; - QmlDebugStream rs(&reply, QIODevice::WriteOnly); - QByteArray cmd = "BREAKPOINTS"; - rs << cmd - << d->breakpoints; - - QString logBreakpoints; - QTextStream str(&logBreakpoints); - str << cmd << " ("; - bool first = true; - foreach (const JSAgentBreakpointData &bp, d->breakpoints) { - if (first) - first = false; - else - str << ", "; - str << '[' << bp.functionName << ", " << bp.fileUrl << ", " << bp.lineNumber << ']'; - } - str << ')'; - d->logSendMessage(logBreakpoints); - - sendMessage(reply); -} - -void QScriptDebuggerClient::assignValueInDebugger(const WatchData *data, - const QString &expr, - const QVariant &valueV) -{ - QByteArray reply; - QmlDebugStream rs(&reply, QIODevice::WriteOnly); - QByteArray cmd = "EXEC"; - rs << cmd; - QString expression = QString(_("%1 = %2;")).arg(expr).arg(valueV.toString()); - rs << data->iname << expression; - d->logSendMessage(QString::fromLatin1("%1 %2 %3 %4"). - arg(QLatin1String(cmd), QLatin1String(data->iname), expr, - valueV.toString())); - sendMessage(reply); -} - -void QScriptDebuggerClient::updateWatchData(const WatchData &data) -{ - QByteArray reply; - QmlDebugStream rs(&reply, QIODevice::WriteOnly); - QByteArray cmd = "EXEC"; - rs << cmd; - rs << data.iname << data.name; - d->logSendMessage(QLatin1String(cmd) + QLatin1Char(' ') + QLatin1String(data.iname) - + QLatin1Char(' ') + data.name); - sendMessage(reply); -} - -void QScriptDebuggerClient::executeDebuggerCommand(const QString &command) -{ - QByteArray reply; - QmlDebugStream rs(&reply, QIODevice::WriteOnly); - QByteArray cmd = "EXEC"; - QByteArray console = "console"; - rs << cmd << console << command; - d->logSendMessage(QLatin1String(cmd) + QLatin1Char(' ') + QLatin1String(console) - + QLatin1Char(' ') + command); - sendMessage(reply); -} - -void QScriptDebuggerClient::synchronizeWatchers(const QStringList &watchers) -{ - // send watchers list - QByteArray reply; - QmlDebugStream rs(&reply, QIODevice::WriteOnly); - QByteArray cmd = "WATCH_EXPRESSIONS"; - rs << cmd; - d->logSendMessage(QString::fromLatin1("%1 (%2)").arg(QLatin1String(cmd), - watchers.join(QLatin1String(", ")))); - sendMessage(reply); -} - -void QScriptDebuggerClient::expandObject(const QByteArray &iname, quint64 objectId) -{ - //Check if id is valid - if (qint64(objectId) == -1) - return; - - QByteArray reply; - QmlDebugStream rs(&reply, QIODevice::WriteOnly); - QByteArray cmd = "EXPAND"; - rs << cmd; - rs << iname << objectId; - d->logSendMessage(QLatin1String(cmd) + QLatin1Char(' ') + QLatin1String(iname) - + QString::number(objectId)); - sendMessage(reply); -} - -void QScriptDebuggerClient::sendPing() -{ - d->ping++; - QByteArray reply; - QmlDebugStream rs(&reply, QIODevice::WriteOnly); - QByteArray cmd = "PING"; - rs << cmd; - rs << d->ping; - d->logSendMessage(QLatin1String(cmd)); - sendMessage(reply); -} - -void QScriptDebuggerClient::messageReceived(const QByteArray &data) -{ - QByteArray rwData = data; - QmlDebugStream stream(&rwData, QIODevice::ReadOnly); - - QByteArray command; - stream >> command; - - WatchHandler *watchHandler = d->engine->watchHandler(); - StackHandler *stackHandler = d->engine->stackHandler(); - - if (command == "STOPPED") { - d->engine->inferiorSpontaneousStop(); - - QString logString = QString::fromLatin1(command); - - JSAgentStackFrames stackFrames; - QList<WatchData> watches; - QList<WatchData> locals; - stream >> stackFrames >> watches >> locals; - - logString += QString::fromLatin1(" (%1 stack frames) (%2 watches) (%3 locals)"). - arg(stackFrames.size()).arg(watches.size()).arg(locals.size()); - - StackFrames ideStackFrames; - for (int i = 0; i != stackFrames.size(); ++i) { - StackFrame frame; - frame.line = stackFrames.at(i).lineNumber; - frame.function = QLatin1String(stackFrames.at(i).functionName); - frame.file = d->engine->toFileInProject(QUrl(QLatin1String(stackFrames.at(i).fileUrl))); - frame.usable = QFileInfo(frame.file).isReadable(); - frame.level = i + 1; - ideStackFrames << frame; - } - - stackHandler->setFrames(ideStackFrames); - - bool becauseOfException; - stream >> becauseOfException; - - logString += becauseOfException ? QLatin1String(" exception") : QLatin1String(" no_exception"); - - if (becauseOfException) { - QString error; - stream >> error; - - logString += QLatin1Char(' '); - logString += error; - d->logReceiveMessage(logString); - - QString msg = stackFrames.isEmpty() - ? tr("<p>An uncaught exception occurred:</p><p>%1</p>") - .arg(error.toHtmlEscaped()) - : tr("<p>An uncaught exception occurred in \"%1\":</p><p>%2</p>") - .arg(QLatin1String(stackFrames.value(0).fileUrl), error.toHtmlEscaped()); - Core::AsynchronousMessageBox::information(tr("Uncaught Exception"), msg); - } else { - QString file; - int line = -1; - - if (!ideStackFrames.isEmpty()) { - file = ideStackFrames.at(0).file; - line = ideStackFrames.at(0).line; - } - - QList<JSAgentBreakpointData> breakpoints(d->breakpoints.toList()); - foreach (const JSAgentBreakpointData &data, breakpoints) { - if (data.fileUrl == QUrl::fromLocalFile(file).toString().toUtf8() && - data.lineNumber == line && - data.functionName == "TEMPORARY") { - breakpoints.removeOne(data); - d->breakpoints = JSAgentBreakpoints::fromList(breakpoints); - synchronizeBreakpoints(); - break; - } - } - - d->logReceiveMessage(logString); - } - - if (!ideStackFrames.isEmpty()) - d->engine->gotoLocation(ideStackFrames.value(0)); - - insertLocalsAndWatches(locals, watches, stackHandler->currentIndex()); - - } else if (command == "RESULT") { - WatchData data; - QByteArray iname; - stream >> iname >> data; - - d->logReceiveMessage(QLatin1String(command) + QLatin1Char(' ') - + QLatin1String(iname) + QLatin1Char(' ') + data.value); - - auto item = new WatchItem(data); - item->iname = iname; - if (iname.startsWith("watch.")) { - watchHandler->insertItem(item); - } else if (iname == "console") { - d->engine->showMessage(item->value, ConsoleOutput); - } else if (iname.startsWith("local.")) { - item->name = item->name.left(item->name.indexOf(QLatin1Char(' '))); - watchHandler->insertItem(item); - } else { - qWarning() << "QmlEngine: Unexcpected result: " << iname << item->value; - } - } else if (command == "EXPANDED") { - QList<WatchData> result; - QByteArray iname; - stream >> iname >> result; - d->logReceiveMessage(QString::fromLatin1("%1 %2 (%3 x watchdata)"). - arg(QLatin1String(command), QLatin1String(iname), - QString::number(result.size()))); - bool needPing = false; - - foreach (WatchData data, result) { - data.iname = iname + '.' + data.exp; - watchHandler->insertItem(new WatchItem(data)); - - if (watchHandler->isExpandedIName(data.iname) && qint64(data.id) != -1) { - needPing = true; - expandObject(data.iname, data.id); - } - } - if (needPing) - sendPing(); - } else if (command == "LOCALS") { - QList<WatchData> locals; - QList<WatchData> watches; - int frameId; - stream >> frameId >> locals; - if (!stream.atEnd()) { // compatibility with jsdebuggeragent from 2.1, 2.2 - stream >> watches; - } - - d->logReceiveMessage(QString::fromLatin1("%1 %2 (%3 x locals) (%4 x watchdata)").arg( - QLatin1String(command), QString::number(frameId), - QString::number(locals.size()), QString::number(watches.size()))); - - insertLocalsAndWatches(locals, watches, frameId); - - } else if (command == "PONG") { - int ping; - stream >> ping; - d->logReceiveMessage(QLatin1String(command) + QLatin1Char(' ') + QString::number(ping)); - } else { - qDebug() << Q_FUNC_INFO << "Unknown command: " << command; - d->logReceiveMessage(QLatin1String(command) + QLatin1String(" UNKNOWN COMMAND!!")); - } - -} - -void QScriptDebuggerClient::insertLocalsAndWatches(QList<WatchData> &locals, - QList<WatchData> &watches, - int stackFrameIndex) -{ - WatchHandler *watchHandler = d->engine->watchHandler(); - watchHandler->removeAllData(); - if (stackFrameIndex < 0) - return; - const StackFrame frame = d->engine->stackHandler()->frameAt(stackFrameIndex); - if (!frame.isUsable()) - return; - - bool needPing = false; - foreach (const WatchData &data, watches) { - auto item = new WatchItem(data); - item->iname = watchHandler->watcherName(data.exp); - watchHandler->insertItem(item); - - if (watchHandler->isExpandedIName(data.iname) && qint64(data.id) != -1) { - needPing = true; - expandObject(data.iname, data.id); - } - } - - foreach (const WatchData &data, locals) { - auto item = new WatchItem(data); - if (item->name == QLatin1String("<no initialized data>")) - item->name = tr("No Local Variables"); - item->iname = "local." + item->exp; - watchHandler->insertItem(item); - - if (watchHandler->isExpandedIName(data.iname) && qint64(data.id) != -1) { - needPing = true; - expandObject(data.iname, data.id); - } - } - - if (needPing) - sendPing(); - emit stackFrameCompleted(); -} - -void QScriptDebuggerClient::setEngine(QmlEngine *engine) -{ - d->engine = engine; - connect(this, &QScriptDebuggerClient::stackFrameCompleted, - engine, &DebuggerEngine::stackFrameCompleted); -} - -void QScriptDebuggerClientPrivate::logSendMessage(const QString &msg) const -{ - if (engine) - engine->logMessage(QLatin1String("QScriptDebuggerClient"), QmlEngine::LogSend, msg); -} - -void QScriptDebuggerClientPrivate::logReceiveMessage(const QString &msg) const -{ - if (engine) - engine->logMessage(QLatin1String("QScriptDebuggerClient"), QmlEngine::LogReceive, msg); -} - -} // Internal -} // Debugger diff --git a/src/plugins/debugger/qml/qscriptdebuggerclient.h b/src/plugins/debugger/qml/qscriptdebuggerclient.h deleted file mode 100644 index a09a1839c2..0000000000 --- a/src/plugins/debugger/qml/qscriptdebuggerclient.h +++ /dev/null @@ -1,99 +0,0 @@ -/**************************************************************************** -** -** 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 QSCRIPTDEBUGGERCLIENT_H -#define QSCRIPTDEBUGGERCLIENT_H - -#include "baseqmldebuggerclient.h" - -namespace Debugger { -namespace Internal { - -class QScriptDebuggerClientPrivate; - -class QScriptDebuggerClient : public BaseQmlDebuggerClient -{ - Q_OBJECT - -public: - QScriptDebuggerClient(QmlDebug::QmlDebugConnection *client); - ~QScriptDebuggerClient(); - - void startSession(); - void endSession(); - void resetSession(); - - void executeStep(); - void executeStepOut(); - void executeNext(); - void executeStepI(); - - void executeRunToLine(const ContextData &data); - - void continueInferior(); - void interruptInferior(); - - void activateFrame(int index); - - void insertBreakpoint(Breakpoint bp, int adjustedLine, - int adjustedColumn = -1); - void removeBreakpoint(Breakpoint bp); - void changeBreakpoint(Breakpoint bp); - void synchronizeBreakpoints(); - - void assignValueInDebugger(const WatchData *data, const QString &expression, - const QVariant &valueV); - - void updateWatchData(const WatchData &data); - void executeDebuggerCommand(const QString &command); - - void synchronizeWatchers(const QStringList &watchers); - - void expandObject(const QByteArray &iname, quint64 objectId); - - void setEngine(QmlEngine *engine); - -protected: - void messageReceived(const QByteArray &data); - -private: - void sendPing(); - void insertLocalsAndWatches(QList<WatchData> &locals, QList<WatchData> &watches, - int stackFrameIndex); - -private: - QScriptDebuggerClientPrivate *d; - friend class QScriptDebuggerClientPrivate; -}; - -} // Internal -} // Debugger - -#endif // QSCRIPTDEBUGGERCLIENT_H diff --git a/src/plugins/debugger/watchhandler.cpp b/src/plugins/debugger/watchhandler.cpp index 61ca2e1d53..6828971500 100644 --- a/src/plugins/debugger/watchhandler.cpp +++ b/src/plugins/debugger/watchhandler.cpp @@ -1268,6 +1268,11 @@ void WatchHandler::notifyUpdateFinished() emit m_model->updateFinished(); } +void WatchHandler::reexpandItems() +{ + m_model->reexpandItems(); +} + void WatchHandler::removeItemByIName(const QByteArray &iname) { WatchItem *item = m_model->findItem(iname); diff --git a/src/plugins/debugger/watchhandler.h b/src/plugins/debugger/watchhandler.h index aacbc6e0c2..890fd4d256 100644 --- a/src/plugins/debugger/watchhandler.h +++ b/src/plugins/debugger/watchhandler.h @@ -201,6 +201,8 @@ public: void notifyUpdateStarted(const QList<QByteArray> &inames = {}); void notifyUpdateFinished(); + void reexpandItems(); + private: WatchModel *m_model; // Owned. }; diff --git a/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp b/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp index f62f05d0bf..3f798035cd 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp +++ b/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp @@ -820,13 +820,15 @@ void JsonFieldPage::ComboBoxField::initializeData(MacroExpander *expander) if (!tmpConditions.at(i)) { tmpItems.removeAt(i); tmpData.removeAt(i); - if (i <= index) + if (i < index && index > 0) --index; } } + + if (index < 0 || index >= tmpData.count()) + index = 0; w->setItems(tmpItems, tmpData); w->setInsertPolicy(QComboBox::NoInsert); - w->setCurrentIndex(index); } diff --git a/src/plugins/qbsprojectmanager/qbsproject.cpp b/src/plugins/qbsprojectmanager/qbsproject.cpp index 853a378e63..5d88012f3d 100644 --- a/src/plugins/qbsprojectmanager/qbsproject.cpp +++ b/src/plugins/qbsprojectmanager/qbsproject.cpp @@ -797,8 +797,6 @@ void QbsProject::updateCppCodeModel() } } - if (pinfo.projectParts().isEmpty()) - return; pinfo.finish(); QtSupport::UiCodeModelManager::update(this, uiFiles); diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp index 62a1018881..4b466aa223 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp @@ -473,6 +473,7 @@ QString PropertyEditorQmlBackend::locateQmlFile(const NodeMetaInfo &info, const static QDir resourcesDir(QStringLiteral(":/propertyEditorQmlSources")); QDir importDir(info.importDirectoryPath() + QLatin1String(Constants::QML_DESIGNER_SUBFOLDER)); + QDir importDirVersion(info.importDirectoryPath() + QStringLiteral(".") + QString::number(info.majorVersion()) + QLatin1String(Constants::QML_DESIGNER_SUBFOLDER)); const QString versionString = QStringLiteral("_") + QString::number(info.majorVersion()) + QStringLiteral("_") @@ -484,6 +485,14 @@ QString PropertyEditorQmlBackend::locateQmlFile(const NodeMetaInfo &info, const //Check for qml files with versions first const QString withoutDirWithVersion = relativePathWithVersion.split(QStringLiteral("/")).last(); + + const QString withoutDir = relativePath.split(QStringLiteral("/")).last(); + + if (importDirVersion.exists(withoutDir)) + return importDirVersion.absoluteFilePath(withoutDir); + + + if (importDir.exists(relativePathWithVersion)) return importDir.absoluteFilePath(relativePathWithVersion); if (importDir.exists(withoutDirWithVersion)) //Since we are in a subfolder of the import we do not require the directory @@ -493,7 +502,7 @@ QString PropertyEditorQmlBackend::locateQmlFile(const NodeMetaInfo &info, const if (resourcesDir.exists(relativePathWithVersion)) return resourcesDir.absoluteFilePath(relativePathWithVersion); - const QString withoutDir = relativePath.split(QStringLiteral("/")).last(); + if (importDir.exists(relativePath)) return importDir.absoluteFilePath(relativePath); if (importDir.exists(withoutDir)) //Since we are in a subfolder of the import we do not require the directory diff --git a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp index d6d95409b7..34f07c22ff 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp @@ -209,6 +209,15 @@ void SubComponentManager::parseDirectories() foreach (const QString &path, importPaths()) { QString fullUrl = path + QLatin1Char('/') + url; dirInfo = QFileInfo(fullUrl); + + if (dirInfo.exists() && dirInfo.isDir()) { + //### todo full qualified names QString nameSpace = import.uri(); + parseDirectory(dirInfo.canonicalFilePath(), false); + } + + QString fullUrlVersion = path + QLatin1Char('/') + url + QLatin1Char('.') + import.version().split(".").first(); + dirInfo = QFileInfo(fullUrlVersion); + if (dirInfo.exists() && dirInfo.isDir()) { //### todo full qualified names QString nameSpace = import.uri(); parseDirectory(dirInfo.canonicalFilePath(), false); diff --git a/src/plugins/qtsupport/uicodemodelsupport.cpp b/src/plugins/qtsupport/uicodemodelsupport.cpp index 0fb3f4154b..358784c1b9 100644 --- a/src/plugins/qtsupport/uicodemodelsupport.cpp +++ b/src/plugins/qtsupport/uicodemodelsupport.cpp @@ -47,8 +47,7 @@ #include <QFile> #include <QFileInfo> - -enum { debug = 0 }; +#include <QLoggingCategory> using namespace ProjectExplorer; using namespace CPlusPlus; @@ -79,20 +78,26 @@ UiCodeModelSupport::UiCodeModelSupport(CppTools::CppModelManager *modelmanager, m_headerFileName(uiHeaderFile), m_state(BARE) { - if (debug) - qDebug()<<"ctor UiCodeModelSupport for"<<m_uiFileName<<uiHeaderFile; + QLoggingCategory log("qtc.qtsupport.uicodemodelsupport"); + qCDebug(log) << "ctor UiCodeModelSupport for" << m_uiFileName << uiHeaderFile; connect(&m_process, SIGNAL(finished(int)), this, SLOT(finishProcess())); + init(); } UiCodeModelSupport::~UiCodeModelSupport() { - if (debug) - qDebug()<<"dtor ~UiCodeModelSupport for"<<m_uiFileName; + disconnect(&m_process, SIGNAL(finished(int)), + this, SLOT(finishProcess())); + m_process.kill(); + CppTools::CppModelManager::instance()->emitAbstractEditorSupportRemoved(m_headerFileName); + QLoggingCategory log("qtc.qtsupport.uicodemodelsupport"); + qCDebug(log) << "dtor ~UiCodeModelSupport for" << m_uiFileName; } void UiCodeModelSupport::init() const { + QLoggingCategory log("qtc.qtsupport.uicodemodelsupport"); if (m_state != BARE) return; QDateTime sourceTime = QFileInfo(m_uiFileName).lastModified(); @@ -101,53 +106,43 @@ void UiCodeModelSupport::init() const if (uiHeaderTime.isValid() && (uiHeaderTime > sourceTime)) { QFile file(m_headerFileName); if (file.open(QFile::ReadOnly | QFile::Text)) { - if (debug) - qDebug()<<"ui*h file is more recent then source file, using information from ui*h file"<<m_headerFileName; + qCDebug(log) << "init: ui*h file is more recent then source file, using information from ui*h file" << m_headerFileName; QTextStream stream(&file); m_contents = stream.readAll().toUtf8(); m_cacheTime = uiHeaderTime; m_state = FINISHED; + notifyAboutUpdatedContents(); return; } } - if (debug) - qDebug()<<"ui*h file not found, or not recent enough, trying to create it on the fly"; + qCDebug(log) << "ui*h file not found, or not recent enough, trying to create it on the fly"; QFile file(m_uiFileName); if (file.open(QFile::ReadOnly | QFile::Text)) { QTextStream stream(&file); const QString contents = stream.readAll(); if (runUic(contents)) { - if (debug) - qDebug()<<"created on the fly"; + qCDebug(log) << "created on the fly"; return; } else { // uic run was unsuccesfull - if (debug) - qDebug()<<"uic run wasn't succesfull"; + qCDebug(log) << "uic run wasn't succesfull"; m_cacheTime = QDateTime (); m_contents.clear(); m_state = FINISHED; + notifyAboutUpdatedContents(); return; } } else { - if (debug) - qDebug()<<"Could open "<<m_uiFileName<<"needed for the cpp model"; + qCDebug(log) << "Could not open " << m_uiFileName << "needed for the cpp model"; m_contents.clear(); m_state = FINISHED; + notifyAboutUpdatedContents(); } } QByteArray UiCodeModelSupport::contents() const { - // Check the common case first - if (m_state == FINISHED) - return m_contents; - if (m_state == BARE) - init(); - if (m_state == RUNNING) - finishProcess(); - return m_contents; } @@ -166,16 +161,20 @@ void UiCodeModelSupport::setHeaderFileName(const QString &name) if (m_headerFileName == name && m_cacheTime.isValid()) return; - if (m_state == RUNNING) - finishProcess(); + if (m_state == RUNNING) { + m_state = ABORTING; + m_process.kill(); + m_process.waitForFinished(3000); + } - if (debug) - qDebug() << "UiCodeModelSupport::setFileName"<<name; + QLoggingCategory log("qtc.qtsupport.uicodemodelsupport"); + qCDebug(log) << "UiCodeModelSupport::setFileName" << name; m_headerFileName = name; m_contents.clear(); m_cacheTime = QDateTime(); m_state = BARE; + init(); } bool UiCodeModelSupport::runUic(const QString &ui) const @@ -183,10 +182,10 @@ bool UiCodeModelSupport::runUic(const QString &ui) const const QString uic = uicCommand(); if (uic.isEmpty()) return false; + QLoggingCategory log("qtc.qtsupport.uicodemodelsupport"); m_process.setEnvironment(environment()); - if (debug) - qDebug() << "UiCodeModelSupport::runUic " << uic << " on " << ui.size() << " bytes"; + qCDebug(log) << " UiCodeModelSupport::runUic " << uic << " on " << ui.size() << " bytes"; m_process.start(uic, QStringList(), QIODevice::ReadWrite); if (!m_process.waitForStarted()) return false; @@ -198,8 +197,7 @@ bool UiCodeModelSupport::runUic(const QString &ui) const return true; error: - if (debug) - qDebug() << "failed" << m_process.readAllStandardError(); + qCDebug(log) << "failed" << m_process.readAllStandardError(); m_process.kill(); m_state = FINISHED; return false; @@ -207,29 +205,26 @@ error: void UiCodeModelSupport::updateFromEditor(const QString &formEditorContents) { - if (m_state == BARE) - init(); - if (m_state == RUNNING) - finishProcess(); - if (runUic(formEditorContents)) - if (finishProcess()) - updateDocument(); + QLoggingCategory log("qtc.qtsupport.uicodemodelsupport"); + qCDebug(log) << "updating from editor" << m_uiFileName; + if (m_state == RUNNING) { + m_state = ABORTING; + m_process.kill(); + m_process.waitForFinished(3000); + } + runUic(formEditorContents); } void UiCodeModelSupport::updateFromBuild() { - if (debug) - qDebug()<<"UiCodeModelSupport::updateFromBuild() for file"<<m_uiFileName; - if (m_state == BARE) - init(); - if (m_state == RUNNING) - finishProcess(); + QLoggingCategory log("qtc.qtsupport.uicodemodelsupport"); + qCDebug(log) << "UiCodeModelSupport::updateFromBuild() for " << m_uiFileName; + // This is mostly a fall back for the cases when uic couldn't be run // it pays special attention to the case where a ui_*h was newly created QDateTime sourceTime = QFileInfo(m_uiFileName).lastModified(); if (m_cacheTime.isValid() && m_cacheTime >= sourceTime) { - if (debug) - qDebug()<<"Cache is still more recent then source"; + qCDebug(log) << "Cache is still more recent then source"; return; } else { QFileInfo fi(m_headerFileName); @@ -237,20 +232,19 @@ void UiCodeModelSupport::updateFromBuild() if (uiHeaderTime.isValid() && (uiHeaderTime > sourceTime)) { if (m_cacheTime >= uiHeaderTime) return; - if (debug) - qDebug()<<"found ui*h updating from it"; + qCDebug(log) << "found ui*h updating from it"; QFile file(m_headerFileName); if (file.open(QFile::ReadOnly | QFile::Text)) { QTextStream stream(&file); m_contents = stream.readAll().toUtf8(); m_cacheTime = uiHeaderTime; + notifyAboutUpdatedContents(); updateDocument(); return; } } - if (debug) - qDebug()<<"ui*h not found or not more recent then source not changing anything"; + qCDebug(log) << "ui*h not found or not more recent then source not changing anything"; } } @@ -279,34 +273,30 @@ QStringList UiCodeModelSupport::environment() const } } -bool UiCodeModelSupport::finishProcess() const +bool UiCodeModelSupport::finishProcess() { if (m_state != RUNNING) return false; + QLoggingCategory log("qtc.qtsupport.uicodemodelsupport"); if (!m_process.waitForFinished(3000) && m_process.exitStatus() != QProcess::NormalExit && m_process.exitCode() != 0) { - if (m_state != RUNNING) // waitForFinished can recurse into finishProcess - return false; - if (debug) - qDebug() << "failed" << m_process.readAllStandardError(); + qCDebug(log) << "finish process: failed" << m_process.readAllStandardError(); m_process.kill(); m_state = FINISHED; return false; } - if (m_state != RUNNING) // waitForFinished can recurse into finishProcess - return true; - // As far as I can discover in the UIC sources, it writes out local 8-bit encoding. The // conversion below is to normalize both the encoding, and the line terminators. QString normalized = QString::fromLocal8Bit(m_process.readAllStandardOutput()); m_contents = normalized.toUtf8(); m_cacheTime = QDateTime::currentDateTime(); - if (debug) - qDebug() << "ok" << m_contents.size() << "bytes."; + qCDebug(log) << "finish process: ok" << m_contents.size() << "bytes."; m_state = FINISHED; + notifyAboutUpdatedContents(); + updateDocument(); return true; } diff --git a/src/plugins/qtsupport/uicodemodelsupport.h b/src/plugins/qtsupport/uicodemodelsupport.h index 333950e79c..91dbdb3361 100644 --- a/src/plugins/qtsupport/uicodemodelsupport.h +++ b/src/plugins/qtsupport/uicodemodelsupport.h @@ -73,11 +73,11 @@ private: QStringList environment() const; private slots: - bool finishProcess() const; + bool finishProcess(); private: ProjectExplorer::Project *m_project; - enum State { BARE, RUNNING, FINISHED }; + enum State { BARE, RUNNING, FINISHED, ABORTING }; void init() const; bool runUic(const QString &ui) const; diff --git a/tests/auto/debugger/tst_dumpers.cpp b/tests/auto/debugger/tst_dumpers.cpp index b4c8054d96..3263ad00b2 100644 --- a/tests/auto/debugger/tst_dumpers.cpp +++ b/tests/auto/debugger/tst_dumpers.cpp @@ -2962,6 +2962,14 @@ void tst_Dumpers::dumper_data() + Check4("url1.d.fragment", "\"\"", "@QString"); + QTest::newRow("QUuid") + << Data("#include <QUuid>", + "QUuid uuid(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);\n" + "unused(&uuid);\n") + + CoreProfile() + + Check("uuid", "{00000001-0002-0003-0405-060708090a0b}", "@QUuid"); + + QByteArray expected1 = "\"AAA"; expected1.append(char('\t')); expected1.append(char('\r')); diff --git a/tests/manual/qml/testfiles/components/MyButton.qml b/tests/manual/qml/testfiles/components/MyButton.qml deleted file mode 100644 index a2da722180..0000000000 --- a/tests/manual/qml/testfiles/components/MyButton.qml +++ /dev/null @@ -1,37 +0,0 @@ -/**************************************************************************** -** -** 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. -** -****************************************************************************/ - -import QtQuick 1.1 - -Rectangle { - width: 64 - height: 48 - color: "Red" -} diff --git a/tests/manual/qml/testfiles/empty.qml b/tests/manual/qml/testfiles/empty.qml deleted file mode 100644 index a59296037f..0000000000 --- a/tests/manual/qml/testfiles/empty.qml +++ /dev/null @@ -1,36 +0,0 @@ -/**************************************************************************** -** -** 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. -** -****************************************************************************/ - -import QtQuick 1.1 - -Rectangle { - width: 640 - height: 480 -} diff --git a/tests/manual/qml/testfiles/flipable.qml b/tests/manual/qml/testfiles/flipable.qml deleted file mode 100644 index 2fc46c4567..0000000000 --- a/tests/manual/qml/testfiles/flipable.qml +++ /dev/null @@ -1,43 +0,0 @@ -/**************************************************************************** -** -** 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. -** -****************************************************************************/ - -import QtQuick 1.1 - -Flipable { - width: 640 - height: 480 - front: Text { - text: "front" - } - back: Text { - text: "back" - } - -} diff --git a/tests/manual/qml/testfiles/helloworld.qml b/tests/manual/qml/testfiles/helloworld.qml deleted file mode 100644 index c0ab2b8bed..0000000000 --- a/tests/manual/qml/testfiles/helloworld.qml +++ /dev/null @@ -1,41 +0,0 @@ -/**************************************************************************** -** -** 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. -** -****************************************************************************/ - -import QtQuick 1.1 - -Rectangle { - width: 200 - height: 200 - Text { - x: 66 - y: 93 - text: "Hello World" - } -} diff --git a/tests/manual/qml/testfiles/helloworld_inverted.qml b/tests/manual/qml/testfiles/helloworld_inverted.qml deleted file mode 100644 index 6bdd3e5b33..0000000000 --- a/tests/manual/qml/testfiles/helloworld_inverted.qml +++ /dev/null @@ -1,43 +0,0 @@ -/**************************************************************************** -** -** 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. -** -****************************************************************************/ - -import QtQuick 1.1 - -Rectangle { - width: 200 - height: 200 - color: "black" - Text { - x: 66 - y: 93 - text: "Hello World" - color: "white" - } -} diff --git a/tests/manual/qml/testfiles/images/qtcreator.ico b/tests/manual/qml/testfiles/images/qtcreator.ico Binary files differdeleted file mode 100644 index 06209ee81e..0000000000 --- a/tests/manual/qml/testfiles/images/qtcreator.ico +++ /dev/null diff --git a/tests/manual/qml/testfiles/images/qtcreator.jpg b/tests/manual/qml/testfiles/images/qtcreator.jpg Binary files differdeleted file mode 100644 index 700af01ff1..0000000000 --- a/tests/manual/qml/testfiles/images/qtcreator.jpg +++ /dev/null diff --git a/tests/manual/qml/testfiles/images/qtcreator.png b/tests/manual/qml/testfiles/images/qtcreator.png Binary files differdeleted file mode 100644 index b1d96b9c43..0000000000 --- a/tests/manual/qml/testfiles/images/qtcreator.png +++ /dev/null diff --git a/tests/manual/qml/testfiles/listmodel.qml b/tests/manual/qml/testfiles/listmodel.qml deleted file mode 100644 index 270bd0a6b2..0000000000 --- a/tests/manual/qml/testfiles/listmodel.qml +++ /dev/null @@ -1,40 +0,0 @@ -/**************************************************************************** -** -** 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. -** -****************************************************************************/ - -import QtQuick 1.1 - -ListModel { - id: myModel - ListElement { - content: "foo" - text: "bar" - } - -} diff --git a/tests/manual/qml/testfiles/listview.qml b/tests/manual/qml/testfiles/listview.qml deleted file mode 100644 index 44f6cd170f..0000000000 --- a/tests/manual/qml/testfiles/listview.qml +++ /dev/null @@ -1,74 +0,0 @@ -/**************************************************************************** -** -** 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. -** -****************************************************************************/ - -import QtQuick 1.1 - -Item { - width: 200 - height: 100 - - ListView { - anchors.fill: parent; - model: ListModel { - ListElement { - name: "BMW" - speed: 200 - } - ListElement { - name: "Mercedes" - speed: 180 - } - ListElement { - name: "Audi" - speed: 190 - } - ListElement { - name: "VW" - speed: 180 - } - } - - - delegate: Item { - height: 40 - Row { - spacing: 10 - Text { - text: name; - font.bold: true - } - - Text { text: "speed: " + speed } - } - - - } - } -} diff --git a/tests/manual/qml/testfiles/states.qml b/tests/manual/qml/testfiles/states.qml deleted file mode 100644 index 1d614cea34..0000000000 --- a/tests/manual/qml/testfiles/states.qml +++ /dev/null @@ -1,74 +0,0 @@ -/**************************************************************************** -** -** 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. -** -****************************************************************************/ - -import QtQuick 1.1 - -Rectangle { - id: rect - width: 200 - height: 200 - Text { - id: text - x: 66 - y: 93 - text: "Base State" - } - states: [ - State { - name: "State1" - PropertyChanges { - target: rect - color: "blue" - } - PropertyChanges { - target: text - text: "State1" - } - }, - State { - name: "State2" - PropertyChanges { - target: rect - color: "gray" - } - PropertyChanges { - target: text - text: "State2" - } - } - ] - - Image { - id: image1 - x: 41 - y: 46 - source: "images/qtcreator.png" - } -} diff --git a/tests/manual/qml/testfiles/subcomponent.qml b/tests/manual/qml/testfiles/subcomponent.qml deleted file mode 100644 index 48d4dd9c12..0000000000 --- a/tests/manual/qml/testfiles/subcomponent.qml +++ /dev/null @@ -1,47 +0,0 @@ -/**************************************************************************** -** -** 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. -** -****************************************************************************/ - -import QtQuick 1.1 - -Rectangle { - width: 640 - height: 480 - Component { - id: redSquare - Rectangle { - color: "red" - width: 100 - height: 100 - } - } - - Loader { sourceComponent: redSquare;} - Loader { sourceComponent: redSquare; x: 20 } -} diff --git a/tests/manual/qml/testfiles/testfiles.qmlproject b/tests/manual/qml/testfiles/testfiles.qmlproject deleted file mode 100644 index 86a56698e2..0000000000 --- a/tests/manual/qml/testfiles/testfiles.qmlproject +++ /dev/null @@ -1,16 +0,0 @@ -// File generated by QtCreator - -import QmlProject 1.0 - -Project { - // Scan current directory for .qml, .js, and image files - QmlFiles { - directory: "." - } - JavaScriptFiles { - directory: "." - } - ImageFiles { - directory: "." - } -} diff --git a/tests/manual/qml/testfiles/usingbutton.qml b/tests/manual/qml/testfiles/usingbutton.qml deleted file mode 100644 index 4d7fc38466..0000000000 --- a/tests/manual/qml/testfiles/usingbutton.qml +++ /dev/null @@ -1,41 +0,0 @@ -/**************************************************************************** -** -** 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. -** -****************************************************************************/ - -import QtQuick 1.1 -import "components" as X - -Rectangle { - width: 640 - height: 480 - - X.MyButton { - } - -} diff --git a/tests/manual/qml/testfiles/webview.qml b/tests/manual/qml/testfiles/webview.qml deleted file mode 100644 index 567b48f568..0000000000 --- a/tests/manual/qml/testfiles/webview.qml +++ /dev/null @@ -1,43 +0,0 @@ -/**************************************************************************** -** -** 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. -** -****************************************************************************/ - -import QtQuick 1.1 -import QtWebKit 1.0 - -// Test loading of import libraries -WebView { - width: 640 - height: 480 - - html:"\ - <body bgcolor=white>\ - Hello World\ - </body>" -} diff --git a/tests/system/objects.map b/tests/system/objects.map index f6ef8dbb4f..e96b9ea1c1 100644 --- a/tests/system/objects.map +++ b/tests/system/objects.map @@ -105,7 +105,7 @@ :Hits_QCLuceneResultWidget {aboveWidget=':Hits_QLabel' type='QCLuceneResultWidget' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Hits_QLabel {text~='\\\\d+ - \\\\d+ of \\\\d+ Hits' type='QLabel' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :JavaScript.QmlProfilerEventsTable_QmlProfiler::Internal::QV8ProfilerEventsMainView {container=':*Qt Creator.JavaScript_QDockWidget' name='QmlProfilerEventsTable' type='QmlProfiler::Internal::QV8ProfilerEventsMainView' visible='1'} -:Kits_QtVersion_QComboBox {container=':qt_tabwidget_stackedwidget_QWidget' occurrence='5' type='QComboBox' unnamed='1' visible='1'} +:Kits_QtVersion_QComboBox {container=':qt_tabwidget_stackedwidget_QWidget' leftWidget=':QtVersionLabel_KitPage' type='QComboBox' unnamed='1' visible='1'} :Locals and Expressions_Debugger::Internal::WatchTreeView {container=':DebugModeWidget.Locals and Expressions_QDockWidget' name='WatchWindow' type='Debugger::Internal::WatchTreeView' visible='1' windowTitle='Locals and Expressions'} :Minimal required Qt version:_QLabel {text='Minimal required Qt version:' type='QLabel' unnamed='1' visible='1' window=':New Text File_ProjectExplorer::JsonWizard'} :New Text File.Add to project:_QLabel {name='projectLabel' text='Add to project:' type='QLabel' visible='1' window=':New Text File_ProjectExplorer::JsonWizard'} @@ -182,6 +182,7 @@ :QtSupport__Internal__QtVersionManager.errorLabel.QLabel {container=':qt_tabwidget_stackedwidget.QtSupport__Internal__QtVersionManager_QtSupport::Internal::QtOptionsPageWidget' name='errorLabel' type='QLabel' visible='1'} :QtSupport__Internal__QtVersionManager.qmake_QLabel {container=':qt_tabwidget_stackedwidget.QtSupport__Internal__QtVersionManager_QtSupport::Internal::QtOptionsPageWidget' name='qmakePath' type='QLabel' visible='1'} :QtSupport__Internal__QtVersionManager.qtdirList_QTreeWidget {container=':qt_tabwidget_stackedwidget.QtSupport__Internal__QtVersionManager_QtSupport::Internal::QtOptionsPageWidget' name='qtdirList' type='QTreeWidget' visible='1'} +:QtVersionLabel_KitPage {container=':qt_tabwidget_stackedwidget_QWidget' text='Qt version:' type='QLabel' unnamed='1' visible='1'} :Restart required.OK_QPushButton {text='OK' type='QPushButton' unnamed='1' visible='1' window=':Restart required_QMessageBox'} :Restart required_QMessageBox {text='The language change will take effect after a restart of Qt Creator.' type='QMessageBox' unnamed='1' visible='1'} :Revert to Saved.Proceed_QPushButton {text='Proceed' type='QPushButton' unnamed='1' visible='1' window=':Revert to Saved_QMessageBox'} diff --git a/tests/system/shared/project.py b/tests/system/shared/project.py index ebd403254e..7df1631dd7 100644 --- a/tests/system/shared/project.py +++ b/tests/system/shared/project.py @@ -270,26 +270,39 @@ def createProject_Qt_Console(path, projectName, checks = True): def createNewQtQuickApplication(workingDir, projectName = None, targets=Targets.desktopTargetClasses(), minimumQtVersion="5.3", - fromWelcome=False): - available = __createProjectOrFileSelectType__(" Application", "Qt Quick Application", fromWelcome) + withControls = False, fromWelcome=False): + if withControls: + template = "Qt Quick Controls Application" + else: + template = "Qt Quick Application" + available = __createProjectOrFileSelectType__(" Application", template, fromWelcome) projectName = __createProjectSetNameAndPath__(workingDir, projectName) requiredQt = __createProjectHandleQtQuickSelection__(minimumQtVersion) __modifyAvailableTargets__(available, requiredQt) checkedTargets = __chooseTargets__(targets, available) snooze(1) - clickButton(waitForObject(":Next_QPushButton")) - __createProjectHandleLastPage__() + if len(checkedTargets): + clickButton(waitForObject(":Next_QPushButton")) + __createProjectHandleLastPage__() + progressBarWait(10000) + else: + clickButton(waitForObject("{type='QPushButton' text='Cancel' visible='1'}")) - progressBarWait(10000) return checkedTargets, projectName -def createNewQtQuickUI(workingDir, qtQuickVersion="1.1"): - __createProjectOrFileSelectType__(" Application", "Qt Quick UI") +def createNewQtQuickUI(workingDir, qtVersion = "5.3", withControls = False): + if withControls: + template = 'Qt Quick Controls UI' + else: + template = 'Qt Quick UI' + __createProjectOrFileSelectType__(" Other Project", template) if workingDir == None: workingDir = tempDir() projectName = __createProjectSetNameAndPath__(workingDir) - __createProjectHandleQtQuickSelection__(qtQuickVersion) + __createProjectHandleQtQuickSelection__(qtVersion) __createProjectHandleLastPage__() + progressBarWait(10000) + return projectName def createNewQmlExtension(workingDir, targets=Targets.DESKTOP_474_GCC, qtQuickVersion=1): diff --git a/tests/system/shared/project_explorer.py b/tests/system/shared/project_explorer.py index 0a7bd0e36e..c68633af22 100644 --- a/tests/system/shared/project_explorer.py +++ b/tests/system/shared/project_explorer.py @@ -217,19 +217,15 @@ def __selectTreeItemOnBuildAndRun__(treeViewOrWidget, itemText, isRegex=False): test.compare(manual.data().toString(), "Manual", "Verifying label for section") if isRegex: pattern = re.compile(itemText) - found = False for section in [autoDetected, manual]: for dumpedItem in dumpItems(model, section): if (isRegex and pattern.match(dumpedItem) or itemText == dumpedItem): - found = True item = ".".join([str(section.data().toString()), dumpedItem.replace(".", "\\.").replace("_", "\\_")]) clickItem(treeViewOrWidget, item, 5, 5, 0, Qt.LeftButton) - break - if found: - break - return found + return True + return False def __getTargetFromToolTip__(toolTip): if toolTip == None or not isinstance(toolTip, (str, unicode)): diff --git a/tests/system/suite_debugger/tst_qml_js_console/test.py b/tests/system/suite_debugger/tst_qml_js_console/test.py index c149eeb4a2..f6729f4c5a 100644 --- a/tests/system/suite_debugger/tst_qml_js_console/test.py +++ b/tests/system/suite_debugger/tst_qml_js_console/test.py @@ -123,7 +123,7 @@ def main(): return qmlProjFile = os.path.join(qmlProjDir, projName) # start Creator by passing a .qmlproject file - startApplication('qtcreator -load QmlProjectManager' + SettingsPath + ' "%s"' % qmlProjFile) + startApplication('qtcreator' + SettingsPath + ' "%s"' % qmlProjFile) if not startedWithoutPluginError(): return @@ -142,10 +142,6 @@ def main(): rootIndex = getQModelIndexStr("text='Rectangle'", ":Locals and Expressions_Debugger::Internal::WatchTreeView") # make sure the items inside the root item are visible - if JIRA.isBugStillOpen(14210): - doubleClick(waitForObject(rootIndex)) - else: - test.warning("QTCREATORBUG-14210 is not open anymore. Can the workaround be removed?") doubleClick(waitForObject(rootIndex)) if not object.exists(":DebugModeWidget_QmlJSTools::Internal::QmlConsoleView"): invokeMenuItem("Window", "Output Panes", "QML/JS Console") @@ -156,14 +152,14 @@ def main(): ("color ='silver'", "silver", "color", u"#\u200bc0c0c0"), ("width=66", "66", "width"), ("anchors.centerIn", "<unnamed object>"), ("opacity", "1"), ("opacity = .2", u"0.\u200b2", "opacity")] - # check green inner Rectangle - runChecks("text='Rectangle'", rootIndex, checks) + # check red inner Rectangle + runChecks("text='Rectangle' occurrence='2'", rootIndex, checks) checks = [("color", u"#\u200bff0000"), ("width", "100"), ("height", "100"), ("radius = Math.min(width, height) / 2", "50", "radius"), ("parent.objectName= 'mainRect'", "mainRect")] - # check red inner Rectangle - runChecks("text='Rectangle' occurrence='2'", rootIndex, checks) + # check green inner Rectangle + runChecks("text='Rectangle'", rootIndex, checks) checks = [("color", u"#\u200b000000"), ("font.pointSize=14", "14", "font.pointSize"), ("font.bold", "false"), ("font.weight=Font.Bold", "75", "font.bold", "true"), diff --git a/tests/system/suite_debugger/tst_qml_locals/test.py b/tests/system/suite_debugger/tst_qml_locals/test.py index a9010d00a3..65291273ce 100644 --- a/tests/system/suite_debugger/tst_qml_locals/test.py +++ b/tests/system/suite_debugger/tst_qml_locals/test.py @@ -45,7 +45,7 @@ def main(): return qmlProjFile = os.path.join(qmlProjDir, projName) # start Creator by passing a .qmlproject file - startApplication('qtcreator -load QmlProjectManager' + SettingsPath + ' "%s"' % qmlProjFile) + startApplication('qtcreator' + SettingsPath + ' "%s"' % qmlProjFile) if not startedWithoutPluginError(): return waitFor('object.exists(":Qt Creator_Utils::NavigationTreeView")', 10000) @@ -91,8 +91,8 @@ def main(): checkForEmptyRows(items) check = [[None, 0, {"Properties":1, "Rectangle":2, "Text":1}, {"width":"360", "height":"360"}], ["Text", 1, {"Properties":1}, {"text":"Check"}], - ["Rectangle", 1, {"Properties":1}, {"width":"50", "height":"50", "color":"#008000"}], - ["Rectangle", 2, {"Properties":1}, {"width":"100", "height":"100", "color":"#ff0000"}] + ["Rectangle", 2, {"Properties":1}, {"width":"50", "height":"50", "color":"#008000"}], + ["Rectangle", 1, {"Properties":1}, {"width":"100", "height":"100", "color":"#ff0000"}] ] for current in check: if current[0]: @@ -108,23 +108,19 @@ def main(): def __unfoldTree__(): rootIndex = getQModelIndexStr("text='Rectangle'", ':Locals and Expressions_Debugger::Internal::WatchTreeView') - if JIRA.isBugStillOpen(14210): - doubleClick(waitForObject(rootIndex)) - else: - test.warning("QTCREATORBUG-14210 is not open anymore. Can the workaround be removed?") unfoldQModelIndexIncludingProperties(rootIndex) - if JIRA.isBugStillOpen(14210): - for item in ["text='Rectangle' occurrence='2'", "text='Rectangle' occurrence='2'", "text='Text'"]: - # both Rectangles will be clicked because they change their order - doubleClick(waitForObject(getQModelIndexStr(item, rootIndex))) - snooze(1) - subItems = ["text='Rectangle' occurrence='2'", "text='Rectangle'", "text='Text'"] + subItems = ["text='Rectangle'", "text='Rectangle' occurrence='2'", "text='Text'"] for item in subItems: unfoldQModelIndexIncludingProperties(getQModelIndexStr(item, rootIndex)) def unfoldQModelIndexIncludingProperties(indexStr): + tv = waitForObject(':Locals and Expressions_Debugger::Internal::WatchTreeView') + # HACK to avoid failing clicks + tv.scrollToBottom() doubleClick(waitForObject(indexStr)) propIndex = getQModelIndexStr("text='Properties'", indexStr) + # HACK to avoid failing clicks + tv.scrollToBottom() doubleClick(waitForObject(propIndex)) def fetchItems(index, valIndex, treeView): diff --git a/tests/system/suite_qtquick/tst_qtquick_creation/test.py b/tests/system/suite_qtquick/tst_qtquick_creation/test.py index 3a3562c22f..e3012d0468 100644 --- a/tests/system/suite_qtquick/tst_qtquick_creation/test.py +++ b/tests/system/suite_qtquick/tst_qtquick_creation/test.py @@ -34,16 +34,31 @@ def main(): startApplication("qtcreator" + SettingsPath) if not startedWithoutPluginError(): return - for targ, qVer in [[Targets.DESKTOP_480_DEFAULT, "1.1"], [Targets.DESKTOP_521_DEFAULT, "2.1"], - [Targets.DESKTOP_521_DEFAULT, "2.2"], [Targets.DESKTOP_531_DEFAULT, "2.3"], - [Targets.DESKTOP_521_DEFAULT, "Controls 1.0"], [Targets.DESKTOP_521_DEFAULT, "Controls 1.1"], - [Targets.DESKTOP_531_DEFAULT, "Controls 1.2"]]: + + available = [("5.3", False), ("5.3", True)] + if platform.system() != 'Darwin': + available.extend([("5.4", False), ("5.4", True)]) + + for qtVersion, controls in available: + if qtVersion == "5.3": + targ = Targets.DESKTOP_531_DEFAULT + quick = "2.3" + else: + targ = Targets.DESKTOP_541_GCC + quick = "2.4" # using a temporary directory won't mess up a potentially existing workingDir = tempDir() checkedTargets, projectName = createNewQtQuickApplication(workingDir, targets=targ, - qtQuickVersion=qVer) - test.log("Building project Qt Quick %s Application (%s)" - % (qVer, Targets.getStringForTarget(targ))) + minimumQtVersion=qtVersion, + withControls = controls) + if len(checkedTargets) == 0: + test.fatal("Could not check wanted target") + continue + additionalText = '' + if controls: + additionalText = ' Controls ' + test.log("Building project Qt Quick%sApplication (%s)" + % (additionalText, Targets.getStringForTarget(targ))) result = modifyRunSettingsForHookInto(projectName, len(checkedTargets), 11223) invokeMenuItem("Build", "Build All") waitForCompile() @@ -57,10 +72,8 @@ def main(): allowAppThroughWinFW(workingDir, projectName) if result: function = "subprocessFunctionQuick2" - if qVer[0] == "1": - function = "subprocessFunctionQuick1" result = runAndCloseApp(True, projectName, 11223, function, - SubprocessType.QT_QUICK_APPLICATION, quickVersion=qVer) + SubprocessType.QT_QUICK_APPLICATION, quickVersion=quick) else: result = runAndCloseApp(sType=SubprocessType.QT_QUICK_APPLICATION) removeExecutableAsAttachableAUT(projectName, 11223) @@ -70,7 +83,9 @@ def main(): if result == None: checkCompile() else: - logApplicationOutput() + appOutput = logApplicationOutput() + test.verify(not ("main.qml" in appOutput or "MainForm.ui.qml" in appOutput), + "Does the Application Output indicate QML errors?") invokeMenuItem("File", "Close All Projects and Editors") invokeMenuItem("File", "Exit") @@ -82,8 +97,5 @@ def subprocessFunctionGenericQuick(quickVersion): test.log("Clicking 'Hello World' Text to close QtQuick%dApplicationViewer" % quickVersion) mouseClick(helloWorldText, 5, 5, 0, Qt.LeftButton) -def subprocessFunctionQuick1(): - subprocessFunctionGenericQuick(1) - def subprocessFunctionQuick2(): subprocessFunctionGenericQuick(2) diff --git a/tests/system/suite_qtquick/tst_qtquick_creation3/test.py b/tests/system/suite_qtquick/tst_qtquick_creation3/test.py index aa3347118d..345d6cae73 100644 --- a/tests/system/suite_qtquick/tst_qtquick_creation3/test.py +++ b/tests/system/suite_qtquick/tst_qtquick_creation3/test.py @@ -31,30 +31,42 @@ source("../../shared/qtcreator.py") def main(): - startApplication("qtcreator -load QmlProjectManager" + SettingsPath) + startApplication("qtcreator" + SettingsPath) if not startedWithoutPluginError(): return - for quickVersion in ["1.1", "2.1", "2.2", "2.3", "Controls 1.0", "Controls 1.1", "Controls 1.2"]: + available = [("5.3", False), ("5.3", True)] + if platform.system() != 'Darwin': + available.extend([("5.4", False), ("5.4", True)]) + + for qtVersion, controls in available: # using a temporary directory won't mess up a potentially existing workingDir = tempDir() - projectName = createNewQtQuickUI(workingDir, quickVersion) + projectName = createNewQtQuickUI(workingDir, qtVersion, controls) switchViewTo(ViewConstants.PROJECTS) clickButton(waitForObject(":*Qt Creator.Add Kit_QPushButton")) - menuItem = Targets.getStringForTarget(Targets.DESKTOP_531_DEFAULT) + if qtVersion == "5.3": + menuItem = Targets.getStringForTarget(Targets.DESKTOP_531_DEFAULT) + quick = "2.3" + else: + menuItem = Targets.getStringForTarget(Targets.DESKTOP_541_GCC) + quick = "2.4" if platform.system() == 'Darwin': waitFor("macHackActivateContextMenuItem(menuItem)", 5000) else: activateItem(waitForObjectItem("{type='QMenu' unnamed='1' visible='1' " "window=':Qt Creator_Core::Internal::MainWindow'}", menuItem)) - test.log("Running project Qt Quick %s UI" % quickVersion) - qmlViewer = modifyRunSettingsForHookIntoQtQuickUI(2, 1, workingDir, projectName, 11223, quickVersion) + additionalText = '' + if controls: + additionalText = ' Controls ' + test.log("Running project Qt Quick%sUI (%s)" % (additionalText, menuItem)) + qmlViewer = modifyRunSettingsForHookIntoQtQuickUI(2, 1, workingDir, projectName, 11223, quick) if qmlViewer!=None: qmlViewerPath = os.path.dirname(qmlViewer) qmlViewer = os.path.basename(qmlViewer) result = addExecutableAsAttachableAUT(qmlViewer, 11223) allowAppThroughWinFW(qmlViewerPath, qmlViewer, None) if result: - result = runAndCloseApp(True, qmlViewer, 11223, sType=SubprocessType.QT_QUICK_UI, quickVersion=quickVersion) + result = runAndCloseApp(True, qmlViewer, 11223, sType=SubprocessType.QT_QUICK_UI, quickVersion=quick) else: result = runAndCloseApp(sType=SubprocessType.QT_QUICK_UI) removeExecutableAsAttachableAUT(qmlViewer, 11223) @@ -64,6 +76,8 @@ def main(): if result == None: checkCompile() else: - logApplicationOutput() + appOutput = logApplicationOutput() + test.verify(not ("untitled.qml" in appOutput or "MainForm.ui.qml" in appOutput), + "Does the Application Output indicate QML errors?") invokeMenuItem("File", "Close All Projects and Editors") invokeMenuItem("File", "Exit") |