aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/.prev_CMakeLists.txt42
-rw-r--r--tools/CMakeLists.txt98
-rw-r--r--tools/qml/.prev_CMakeLists.txt72
-rw-r--r--tools/qml/CMakeLists.txt84
-rw-r--r--tools/qml/ResizeItemToWindow.qml20
-rw-r--r--tools/qml/ResizeWindowToItem.qml18
-rw-r--r--tools/qml/conf.h40
-rw-r--r--tools/qml/conf/content/resizeItemToWindow.qml70
-rw-r--r--tools/qml/conf/content/resizeWindowToItem.qml65
-rw-r--r--tools/qml/conf/default.qml57
-rw-r--r--tools/qml/conf/resizeToItem.qml57
-rw-r--r--tools/qml/default.qml10
-rw-r--r--tools/qml/main.cpp243
-rw-r--r--tools/qml/qml.qrc9
-rw-r--r--tools/qml/resizeToItem.qml10
-rw-r--r--tools/qmlcachegen/.prev_CMakeLists.txt46
-rw-r--r--tools/qmlcachegen/CMakeLists.txt8
-rw-r--r--tools/qmlcachegen/Qt6QuickCompilerConfig.cmake.in7
-rw-r--r--tools/qmlcachegen/qmlcache.prf2
-rw-r--r--tools/qmlcachegen/qmlcachegen.cpp213
-rw-r--r--tools/qmlcachegen/qtquickcompiler.prf5
-rw-r--r--tools/qmldom/CMakeLists.txt18
-rw-r--r--tools/qmldom/qmldomtool.cpp324
-rw-r--r--tools/qmleasing/Button.qml47
-rw-r--r--tools/qmleasing/CMakeLists.txt7
-rw-r--r--tools/qmleasing/main.cpp29
-rw-r--r--tools/qmleasing/mainwindow.cpp39
-rw-r--r--tools/qmleasing/mainwindow.h35
-rw-r--r--tools/qmleasing/preview.qml40
-rw-r--r--tools/qmleasing/resources.qrc6
-rw-r--r--tools/qmleasing/segmentproperties.cpp43
-rw-r--r--tools/qmleasing/segmentproperties.h37
-rw-r--r--tools/qmleasing/splineeditor.cpp59
-rw-r--r--tools/qmleasing/splineeditor.h35
-rw-r--r--tools/qmlformat/.prev_CMakeLists.txt21
-rw-r--r--tools/qmlformat/CMakeLists.txt15
-rw-r--r--tools/qmlformat/commentastvisitor.cpp285
-rw-r--r--tools/qmlformat/commentastvisitor.h143
-rw-r--r--tools/qmlformat/dumpastvisitor.cpp1433
-rw-r--r--tools/qmlformat/dumpastvisitor.h161
-rw-r--r--tools/qmlformat/main.cpp293
-rw-r--r--tools/qmlformat/qmlformat.cpp390
-rw-r--r--tools/qmlformat/restructureastvisitor.cpp194
-rw-r--r--tools/qmlformat/restructureastvisitor.h47
-rw-r--r--tools/qmlimportscanner/.prev_CMakeLists.txt62
-rw-r--r--tools/qmlimportscanner/CMakeLists.txt16
-rw-r--r--tools/qmlimportscanner/main.cpp553
-rw-r--r--tools/qmljs/.prev_CMakeLists.txt89
-rw-r--r--tools/qmljs/CMakeLists.txt32
-rw-r--r--tools/qmljs/qmljs.cpp124
-rw-r--r--tools/qmljsrootgen/CMakeLists.txt15
-rw-r--r--tools/qmljsrootgen/main.cpp407
-rw-r--r--tools/qmllint/.prev_CMakeLists.txt23
-rw-r--r--tools/qmllint/CMakeLists.txt11
-rw-r--r--tools/qmllint/checkidentifiers.cpp465
-rw-r--r--tools/qmllint/checkidentifiers.h78
-rw-r--r--tools/qmllint/findwarnings.cpp591
-rw-r--r--tools/qmllint/findwarnings.h127
-rw-r--r--tools/qmllint/main.cpp633
-rw-r--r--tools/qmllint/qcoloroutput.cpp336
-rw-r--r--tools/qmllint/qcoloroutput.h119
-rw-r--r--tools/qmlls/CMakeLists.txt23
-rw-r--r--tools/qmlls/qmllanguageservertool.cpp315
-rw-r--r--tools/qmlplugindump/.prev_CMakeLists.txt39
-rw-r--r--tools/qmlplugindump/CMakeLists.txt10
-rw-r--r--tools/qmlplugindump/main.cpp301
-rw-r--r--tools/qmlplugindump/qmltypereader.cpp29
-rw-r--r--tools/qmlplugindump/qmltypereader.h29
-rw-r--r--tools/qmlpreview/.prev_CMakeLists.txt20
-rw-r--r--tools/qmlpreview/CMakeLists.txt14
-rw-r--r--tools/qmlpreview/main.cpp29
-rw-r--r--tools/qmlpreview/qmlpreviewapplication.cpp30
-rw-r--r--tools/qmlpreview/qmlpreviewapplication.h29
-rw-r--r--tools/qmlpreview/qmlpreviewfilesystemwatcher.cpp31
-rw-r--r--tools/qmlpreview/qmlpreviewfilesystemwatcher.h31
-rw-r--r--tools/qmlprofiler/.prev_CMakeLists.txt23
-rw-r--r--tools/qmlprofiler/CMakeLists.txt6
-rw-r--r--tools/qmlprofiler/commandlistener.cpp29
-rw-r--r--tools/qmlprofiler/commandlistener.h31
-rw-r--r--tools/qmlprofiler/constants.h29
-rw-r--r--tools/qmlprofiler/main.cpp29
-rw-r--r--tools/qmlprofiler/qmlprofilerapplication.cpp34
-rw-r--r--tools/qmlprofiler/qmlprofilerapplication.h31
-rw-r--r--tools/qmlprofiler/qmlprofilerclient.cpp29
-rw-r--r--tools/qmlprofiler/qmlprofilerclient.h31
-rw-r--r--tools/qmlprofiler/qmlprofilerdata.cpp245
-rw-r--r--tools/qmlprofiler/qmlprofilerdata.h33
-rw-r--r--tools/qmlscene/.prev_CMakeLists.txt37
-rw-r--r--tools/qmlscene/CMakeLists.txt21
-rw-r--r--tools/qmlscene/main.cpp45
-rw-r--r--tools/qmltc/CMakeLists.txt27
-rw-r--r--tools/qmltc/main.cpp312
-rw-r--r--tools/qmltc/qmltccodewriter.cpp569
-rw-r--r--tools/qmltc/qmltccodewriter.h39
-rw-r--r--tools/qmltc/qmltccommandlineutils.cpp69
-rw-r--r--tools/qmltc/qmltccommandlineutils.h17
-rw-r--r--tools/qmltc/qmltccompiler.cpp2053
-rw-r--r--tools/qmltc/qmltccompiler.h206
-rw-r--r--tools/qmltc/qmltccompilerpieces.cpp352
-rw-r--r--tools/qmltc/qmltccompilerpieces.h714
-rw-r--r--tools/qmltc/qmltcoutputir.h195
-rw-r--r--tools/qmltc/qmltcoutputprimitives.h100
-rw-r--r--tools/qmltc/qmltcpropertyutils.h55
-rw-r--r--tools/qmltc/qmltctyperesolver.cpp56
-rw-r--r--tools/qmltc/qmltctyperesolver.h40
-rw-r--r--tools/qmltc/qmltcvisitor.cpp871
-rw-r--r--tools/qmltc/qmltcvisitor.h191
-rw-r--r--tools/qmltestrunner/.prev_CMakeLists.txt19
-rw-r--r--tools/qmltestrunner/CMakeLists.txt6
-rw-r--r--tools/qmltestrunner/main.cpp29
-rw-r--r--tools/qmltime/.prev_CMakeLists.txt32
-rw-r--r--tools/qmltime/CMakeLists.txt29
-rw-r--r--tools/qmltime/example.qml29
-rw-r--r--tools/qmltime/linelaidout.qml29
-rw-r--r--tools/qmltime/qmltime.cpp29
-rw-r--r--tools/qmltime/qmltime.h30
-rw-r--r--tools/qmltime/tests/anchors/empty.qml29
-rw-r--r--tools/qmltime/tests/anchors/fill.qml29
-rw-r--r--tools/qmltime/tests/anchors/null.qml29
-rw-r--r--tools/qmltime/tests/animation/large.qml29
-rw-r--r--tools/qmltime/tests/animation/largeNoProps.qml29
-rw-r--r--tools/qmltime/tests/item_creation/children.qml29
-rw-r--r--tools/qmltime/tests/item_creation/data.qml29
-rw-r--r--tools/qmltime/tests/item_creation/no_creation.qml29
-rw-r--r--tools/qmltime/tests/item_creation/resources.qml29
-rw-r--r--tools/qmltime/tests/loader/Loaded.qml29
-rw-r--r--tools/qmltime/tests/loader/component_loader.qml29
-rw-r--r--tools/qmltime/tests/loader/empty_loader.qml29
-rw-r--r--tools/qmltime/tests/loader/no_loader.qml29
-rw-r--r--tools/qmltime/tests/loader/source_loader.qml29
-rw-r--r--tools/qmltime/tests/positioner_creation/no_positioner.qml29
-rw-r--r--tools/qmltime/tests/positioner_creation/null_positioner.qml29
-rw-r--r--tools/qmltime/tests/positioner_creation/positioner.qml29
-rw-r--r--tools/qmltime/tests/vmemetaobject/null.qml29
-rw-r--r--tools/qmltime/tests/vmemetaobject/property.qml29
-rw-r--r--tools/qmltime/textingrid.qml29
-rw-r--r--tools/qmltyperegistrar/CMakeLists.txt28
-rw-r--r--tools/qmltyperegistrar/qmltyperegistrar.cpp207
-rw-r--r--tools/qmltyperegistrar/qmltypes.prf137
-rw-r--r--tools/svgtoqml/CMakeLists.txt32
-rw-r--r--tools/svgtoqml/main.cpp113
-rw-r--r--tools/svgtoqml/main.qml21
142 files changed, 9853 insertions, 7504 deletions
diff --git a/tools/.prev_CMakeLists.txt b/tools/.prev_CMakeLists.txt
deleted file mode 100644
index 324da4ee59..0000000000
--- a/tools/.prev_CMakeLists.txt
+++ /dev/null
@@ -1,42 +0,0 @@
-# Generated from tools.pro.
-
-
-qt_exclude_tool_directories_from_default_target(
- qmlprofiler
- qmlplugindump
- qmleasing
-)
-
-if(QT_FEATURE_qml_devtools)
- add_subdirectory(qmllint)
- add_subdirectory(qmlimportscanner)
- add_subdirectory(qmlformat)
-endif()
-if(QT_FEATURE_qml_devtools AND QT_FEATURE_xmlstreamwriter)
- add_subdirectory(qmlcachegen)
-endif()
-if(QT_FEATURE_thread AND NOT ANDROID AND NOT WASM AND NOT rtems)
- add_subdirectory(qml)
-endif()
-if(QT_FEATURE_qml_profiler AND QT_FEATURE_thread AND NOT ANDROID AND NOT WASM AND NOT rtems)
- add_subdirectory(qmlprofiler)
-endif()
-if(QT_FEATURE_qml_preview AND QT_FEATURE_thread AND NOT ANDROID AND NOT WASM AND NOT rtems)
- add_subdirectory(qmlpreview)
-endif()
-if(QT_BUILD_SHARED_LIBS AND QT_FEATURE_thread AND TARGET Qt::Quick AND NOT ANDROID AND NOT WASM AND NOT rtems)
- add_subdirectory(qmlscene)
- add_subdirectory(qmltime)
-endif()
-if(QT_BUILD_SHARED_LIBS AND QT_FEATURE_process AND QT_FEATURE_regularexpression AND QT_FEATURE_thread AND TARGET Qt::Quick AND NOT ANDROID AND NOT WASM AND NOT rtems)
- add_subdirectory(qmlplugindump)
-endif()
-if(QT_FEATURE_dialogbuttonbox AND QT_FEATURE_thread AND TARGET Qt::Quick AND TARGET Qt::Widgets AND NOT ANDROID AND NOT WASM AND NOT rtems)
- add_subdirectory(qmleasing)
-endif()
-if(QT_FEATURE_thread AND TARGET Qt::QuickTest AND NOT ANDROID AND NOT WASM AND NOT rtems)
- add_subdirectory(qmltestrunner)
-endif()
-if(QT_FEATURE_private_tests AND QT_FEATURE_thread AND NOT ANDROID AND NOT WASM AND NOT rtems)
- add_subdirectory(qmljs)
-endif()
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
index 8438e8c6fa..0b89bea46e 100644
--- a/tools/CMakeLists.txt
+++ b/tools/CMakeLists.txt
@@ -1,46 +1,72 @@
-# Generated from tools.pro.
-
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
-qt_exclude_tool_directories_from_default_target(
- qmlprofiler
- qmlplugindump
- qmleasing
-)
+# Generated from tools.pro.
-if(QT_FEATURE_qml_devtools)
+add_subdirectory(qmldom)
+if(QT_FEATURE_commandlineparser)
add_subdirectory(qmllint)
- add_subdirectory(qmlimportscanner)
- add_subdirectory(qmlformat)
+ add_subdirectory(qmltc)
+ add_subdirectory(qmltyperegistrar)
+ add_subdirectory(qmljsrootgen)
+endif()
+add_subdirectory(qmlimportscanner)
+add_subdirectory(qmlformat)
+if(TARGET Qt::LanguageServerPrivate AND QT_FEATURE_commandlineparser AND QT_FEATURE_filesystemwatcher)
+ if (NOT CMAKE_CROSSCOMPILING OR QT_FORCE_BUILD_TOOLS)
+ add_subdirectory(qmlls)
+ endif()
endif()
-if(QT_FEATURE_qml_devtools AND QT_FEATURE_xmlstreamwriter)
+if(QT_FEATURE_xmlstreamwriter)
# special case begin
# Do not build qmlcachegen here but build it at src/
# time, so that we can use it for our own .qml files in src/imports.
# add_subdirectory(qmlcachegen)
# special case end
endif()
-if(QT_FEATURE_thread AND NOT ANDROID AND NOT WASM AND NOT rtems)
- add_subdirectory(qml)
-endif()
-if(QT_FEATURE_qml_profiler AND QT_FEATURE_thread AND NOT ANDROID AND NOT WASM AND NOT rtems)
- add_subdirectory(qmlprofiler)
-endif()
-if(QT_FEATURE_qml_preview AND QT_FEATURE_thread AND NOT ANDROID AND NOT WASM AND NOT rtems)
- add_subdirectory(qmlpreview)
-endif()
-if(QT_BUILD_SHARED_LIBS AND QT_FEATURE_thread AND TARGET Qt::Quick AND NOT ANDROID AND NOT WASM AND NOT rtems)
- add_subdirectory(qmlscene)
- add_subdirectory(qmltime)
-endif()
-if(QT_BUILD_SHARED_LIBS AND QT_FEATURE_process AND QT_FEATURE_regularexpression AND QT_FEATURE_thread AND TARGET Qt::Quick AND NOT ANDROID AND NOT WASM AND NOT rtems)
- add_subdirectory(qmlplugindump)
-endif()
-if(QT_FEATURE_dialogbuttonbox AND QT_FEATURE_thread AND TARGET Qt::Quick AND TARGET Qt::Widgets AND NOT ANDROID AND NOT WASM AND NOT rtems)
- add_subdirectory(qmleasing)
-endif()
-if(QT_FEATURE_thread AND TARGET Qt::QuickTest AND NOT ANDROID AND NOT WASM AND NOT rtems)
- add_subdirectory(qmltestrunner)
-endif()
-if(QT_FEATURE_private_tests AND QT_FEATURE_thread AND NOT ANDROID AND NOT WASM AND NOT rtems)
- add_subdirectory(qmljs)
-endif()
+
+if(NOT (ANDROID OR WASM OR IOS OR VISIONOS OR rtems))
+ if(QT_FEATURE_thread)
+ add_subdirectory(qml)
+ if(QT_FEATURE_qml_profiler)
+ add_subdirectory(qmlprofiler)
+ endif()
+ if(QT_FEATURE_qml_preview)
+ add_subdirectory(qmlpreview)
+ endif()
+ if(QT_BUILD_SHARED_LIBS AND TARGET Qt::Quick)
+ add_subdirectory(qmlscene)
+ add_subdirectory(qmltime)
+ endif()
+ if(QT_BUILD_SHARED_LIBS
+ AND QT_FEATURE_process
+ AND QT_FEATURE_regularexpression
+ AND TARGET Qt::Quick)
+ add_subdirectory(qmlplugindump)
+ endif()
+ if(TARGET Qt::QuickTest)
+ add_subdirectory(qmltestrunner)
+ endif()
+ if(QT_FEATURE_private_tests)
+ add_subdirectory(qmljs)
+ endif()
+ endif() # QT_FEATURE_thread
+
+ if(TARGET Qt::Quick
+ AND TARGET Qt::Widgets
+ AND QT_FEATURE_checkbox
+ AND QT_FEATURE_combobox
+ AND QT_FEATURE_dialogbuttonbox
+ AND QT_FEATURE_formlayout
+ AND QT_FEATURE_groupbox
+ AND QT_FEATURE_lineedit
+ AND QT_FEATURE_mainwindow
+ AND QT_FEATURE_spinbox
+ AND QT_FEATURE_textedit)
+ add_subdirectory(qmleasing)
+ endif()
+
+ if(TARGET Qt::Quick AND TARGET Qt::Svg)
+ add_subdirectory(svgtoqml)
+ endif()
+endif() # NOT (ANDROID OR WASM OR IOS OR rtems)
diff --git a/tools/qml/.prev_CMakeLists.txt b/tools/qml/.prev_CMakeLists.txt
deleted file mode 100644
index 7a278d970e..0000000000
--- a/tools/qml/.prev_CMakeLists.txt
+++ /dev/null
@@ -1,72 +0,0 @@
-# Generated from qml.pro.
-
-#####################################################################
-## qml Tool:
-#####################################################################
-
-qt_get_tool_target_name(target_name qml)
-qt_internal_add_tool(${target_name}
- TARGET_DESCRIPTION "QML Runtime"
- SOURCES
- conf.h
- main.cpp
- PUBLIC_LIBRARIES
- Qt::CorePrivate
- Qt::QmlPrivate
-)
-
-# Resources:
-set(qml_resource_files
- "conf/content/resizeItemToWindow.qml"
- "conf/content/resizeWindowToItem.qml"
- "conf/default.qml"
- "conf/resizeToItem.qml"
- "resources/qml-64.png"
-)
-
-qt_internal_add_resource(${target_name} "qml"
- PREFIX
- "/qt-project.org/QmlRuntime"
- FILES
- ${qml_resource_files}
-)
-
-
-#### Keys ignored in scope 1:.:.:qml.pro:<TRUE>:
-# ICON = "resources/qml-64.png"
-# QMAKE_TARGET_DESCRIPTION = "QML" "Runtime"
-# QML_IMPORT_NAME = "QmlRuntime.Config"
-# QML_IMPORT_VERSION = "1.0"
-
-## Scopes:
-#####################################################################
-
-qt_internal_extend_target(${target_name} CONDITION TARGET Qt::Gui
- PUBLIC_LIBRARIES
- Qt::Gui
-)
-
-qt_internal_extend_target(${target_name} CONDITION TARGET Qt::Widgets
- PUBLIC_LIBRARIES
- Qt::Widgets
-)
-
-#### Keys ignored in scope 4:.:.:qml.pro:WIN32:
-# RC_ICONS = "resources/qml.ico"
-
-#### Keys ignored in scope 5:.:.:qml.pro:APPLE:
-# ICON = "resources/qml.icns"
-# OTHER_FILES = "resources/Info.plist"
-# QMAKE_INFO_PLIST = "resources/Info.plist"
-
-qt_internal_extend_target(${target_name} CONDITION QT_FEATURE_qml_debug
- DEFINES
- QT_QML_DEBUG_NO_WARNING
-)
-
-set_target_properties(${target_name} PROPERTIES
- QT_QML_MODULE_VERSION 1.0
- QT_QML_MODULE_URI QmlRuntime.Config
-)
-
-qt6_qml_type_registration(${target_name})
diff --git a/tools/qml/CMakeLists.txt b/tools/qml/CMakeLists.txt
index 96d030074d..b209c730d3 100644
--- a/tools/qml/CMakeLists.txt
+++ b/tools/qml/CMakeLists.txt
@@ -1,89 +1,49 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qml.pro.
#####################################################################
-## qml Tool:
+## qml App:
#####################################################################
-qt_get_tool_target_name(target_name qml)
-qt_internal_add_tool(${target_name}
+qt_internal_add_app(qml
TARGET_DESCRIPTION "QML Runtime"
- TOOLS_TARGET Qml # special case
SOURCES
conf.h
main.cpp
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::CorePrivate
Qt::QmlPrivate
)
-# special case begin
-# Don't set properties on a host tool when cross compiling, because it
-# is not being built.
-if(CMAKE_CROSSCOMPILING AND NOT QT_BUILD_TOOLS_WHEN_CROSSCOMPILING)
- return()
-endif()
-
-set_source_files_properties(
- conf/default.qml
- conf/resizeToItem.qml
- conf/content/resizeItemToWindow.qml
- conf/content/resizeWindowToItem.qml
- PROPERTIES QT_SKIP_QUICKCOMPILER 1
-)
-# special case end
-
-# Resources:
-set(qml_resource_files
- "conf/content/resizeItemToWindow.qml"
- "conf/content/resizeWindowToItem.qml"
- "conf/default.qml"
- "conf/resizeToItem.qml"
- "resources/qml-64.png"
-)
+set_target_properties(qml PROPERTIES WIN32_EXECUTABLE FALSE)
-qt_internal_add_resource(${target_name} "qml"
- PREFIX
- "/qt-project.org/QmlRuntime"
- FILES
- ${qml_resource_files}
+# Turn the tool into its own self-contained qml module
+qt6_add_qml_module(qml
+ RESOURCE_PREFIX "/qt-project.org/imports"
+ URI QmlRuntime.Config
+ VERSION 1.0
+ QML_FILES
+ default.qml
+ resizeToItem.qml
+ ResizeItemToWindow.qml
+ ResizeWindowToItem.qml
+ RESOURCES
+ resources/qml-64.png
)
-
-#### Keys ignored in scope 1:.:.:qml.pro:<TRUE>:
-# ICON = "resources/qml-64.png"
-# QMAKE_TARGET_DESCRIPTION = "QML" "Runtime"
-# QML_IMPORT_NAME = "QmlRuntime.Config"
-# QML_IMPORT_VERSION = "1.0"
-
-## Scopes:
-#####################################################################
-
-qt_internal_extend_target(${target_name} CONDITION TARGET Qt::Gui
+qt_internal_extend_target(qml CONDITION TARGET Qt::Gui
PUBLIC_LIBRARIES
Qt::Gui
)
-qt_internal_extend_target(${target_name} CONDITION TARGET Qt::Widgets
+qt_internal_extend_target(qml CONDITION TARGET Qt::Widgets
PUBLIC_LIBRARIES
Qt::Widgets
)
-#### Keys ignored in scope 4:.:.:qml.pro:WIN32:
-# RC_ICONS = "resources/qml.ico"
-
-#### Keys ignored in scope 5:.:.:qml.pro:APPLE:
-# ICON = "resources/qml.icns"
-# OTHER_FILES = "resources/Info.plist"
-# QMAKE_INFO_PLIST = "resources/Info.plist"
-
-qt_internal_extend_target(${target_name} CONDITION QT_FEATURE_qml_debug
+qt_internal_extend_target(qml CONDITION QT_FEATURE_qml_debug
DEFINES
QT_QML_DEBUG_NO_WARNING
)
-
-set_target_properties(${target_name} PROPERTIES
- QT_QML_MODULE_VERSION 1.0
- QT_QML_MODULE_URI QmlRuntime.Config
-)
-
-qt6_qml_type_registration(${target_name})
diff --git a/tools/qml/ResizeItemToWindow.qml b/tools/qml/ResizeItemToWindow.qml
new file mode 100644
index 0000000000..a4d8bfec40
--- /dev/null
+++ b/tools/qml/ResizeItemToWindow.qml
@@ -0,0 +1,20 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+import QtQuick.Window 2.0
+import QtQuick 2.0
+
+Window {
+ property Item containedObject: null
+ onContainedObjectChanged: {
+ if (containedObject == undefined || containedObject == null) {
+ visible = false;
+ return;
+ }
+ width = containedObject.width;
+ height = containedObject.height;
+ containedObject.parent = contentItem;
+ visible = true;
+ }
+ onWidthChanged: if (containedObject) containedObject.width = width
+ onHeightChanged: if (containedObject) containedObject.height = height
+}
diff --git a/tools/qml/ResizeWindowToItem.qml b/tools/qml/ResizeWindowToItem.qml
new file mode 100644
index 0000000000..b969971bc2
--- /dev/null
+++ b/tools/qml/ResizeWindowToItem.qml
@@ -0,0 +1,18 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+import QtQuick.Window 2.0
+import QtQuick 2.0
+
+Window {
+ property Item containedObject: null
+ onContainedObjectChanged: {
+ if (containedObject == undefined || containedObject == null) {
+ visible = false;
+ return;
+ }
+ width = Qt.binding(function() { return containedObject.width });
+ height = Qt.binding(function() { return containedObject.height });
+ containedObject.parent = contentItem;
+ visible = true;
+ }
+}
diff --git a/tools/qml/conf.h b/tools/qml/conf.h
index 84167c9134..d105359037 100644
--- a/tools/qml/conf.h
+++ b/tools/qml/conf.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Research In Motion.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Research In Motion.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef CONF_H
#define CONF_H
@@ -42,7 +17,7 @@ class PartialScene : public QObject
QML_ELEMENT
QML_ADDED_IN_VERSION(1, 0)
public:
- PartialScene(QObject *parent = 0) : QObject(parent)
+ PartialScene(QObject *parent = nullptr) : QObject(parent)
{}
const QUrl container() const { return m_container; }
@@ -52,16 +27,16 @@ public:
if (a==m_container)
return;
m_container = a;
- emit containerChanged();
+ Q_EMIT containerChanged();
}
void setItemType(const QString &a) {
if (a==m_itemType)
return;
m_itemType = a;
- emit itemTypeChanged();
+ Q_EMIT itemTypeChanged();
}
-signals:
+Q_SIGNALS:
void containerChanged();
void itemTypeChanged();
@@ -78,8 +53,7 @@ class Config : public QObject
QML_NAMED_ELEMENT(Configuration)
QML_ADDED_IN_VERSION(1, 0)
public:
- Config (QObject* parent=0) : QObject(parent)
- {}
+ Config (QObject *parent = nullptr) : QObject(parent) {}
QQmlListProperty<PartialScene> sceneCompleters()
{
diff --git a/tools/qml/conf/content/resizeItemToWindow.qml b/tools/qml/conf/content/resizeItemToWindow.qml
deleted file mode 100644
index a645cf8ea9..0000000000
--- a/tools/qml/conf/content/resizeItemToWindow.qml
+++ /dev/null
@@ -1,70 +0,0 @@
-/*****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQuick module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-*****************************************************************************/
-import QtQuick.Window 2.0
-import QtQuick 2.0
-
-Window {
- property Item containedObject: null
- property bool __resizeGuard: false
- onContainedObjectChanged: {
- if (containedObject == undefined || containedObject == null) {
- visible = false;
- return;
- }
- __resizeGuard = true
- width = containedObject.width;
- height = containedObject.height;
- containedObject.parent = contentItem;
- visible = true;
- __resizeGuard = false
- }
- onWidthChanged: if (!__resizeGuard && containedObject) containedObject.width = width
- onHeightChanged: if (!__resizeGuard && containedObject) containedObject.height = height
-}
diff --git a/tools/qml/conf/content/resizeWindowToItem.qml b/tools/qml/conf/content/resizeWindowToItem.qml
deleted file mode 100644
index cd03e5065a..0000000000
--- a/tools/qml/conf/content/resizeWindowToItem.qml
+++ /dev/null
@@ -1,65 +0,0 @@
-/*****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQuick module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-*****************************************************************************/
-import QtQuick.Window 2.0
-import QtQuick 2.0
-
-Window {
- property Item containedObject: null
- onContainedObjectChanged: {
- if (containedObject == undefined || containedObject == null) {
- visible = false;
- return;
- }
- width = Qt.binding(function() { return containedObject.width });
- height = Qt.binding(function() { return containedObject.height });
- containedObject.parent = contentItem;
- visible = true;
- }
-}
diff --git a/tools/qml/conf/default.qml b/tools/qml/conf/default.qml
deleted file mode 100644
index 5c107b2876..0000000000
--- a/tools/qml/conf/default.qml
+++ /dev/null
@@ -1,57 +0,0 @@
-/*****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQuick module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-*****************************************************************************/
-import QmlRuntime.Config 1.0
-
-Configuration {
- PartialScene {
- itemType: "QQuickItem"
- container: "qrc:/qt-project.org/QmlRuntime/conf/content/resizeItemToWindow.qml"
- }
-}
diff --git a/tools/qml/conf/resizeToItem.qml b/tools/qml/conf/resizeToItem.qml
deleted file mode 100644
index 480995a6b0..0000000000
--- a/tools/qml/conf/resizeToItem.qml
+++ /dev/null
@@ -1,57 +0,0 @@
-/*****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQuick module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-*****************************************************************************/
-import QmlRuntime.Config 1.0
-
-Configuration {
- PartialScene {
- itemType: "QQuickItem"
- container: "content/resizeWindowToItem.qml"
- }
-}
diff --git a/tools/qml/default.qml b/tools/qml/default.qml
new file mode 100644
index 0000000000..54a521193c
--- /dev/null
+++ b/tools/qml/default.qml
@@ -0,0 +1,10 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+import QmlRuntime.Config
+
+Configuration {
+ PartialScene {
+ itemType: "QQuickItem"
+ container: "qrc:/qt-project.org/imports/QmlRuntime/Config/ResizeItemToWindow.qml"
+ }
+}
diff --git a/tools/qml/main.cpp b/tools/qml/main.cpp
index 8e5a493bcd..da544c5563 100644
--- a/tools/qml/main.cpp
+++ b/tools/qml/main.cpp
@@ -1,31 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Research In Motion.
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Research In Motion.
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "conf.h"
@@ -61,6 +36,7 @@
#include <qqmlfileselector.h>
#include <private/qtqmlglobal_p.h>
+#include <private/qqmlimport_p.h>
#if QT_CONFIG(qml_animation)
#include <private/qabstractanimation_p.h>
#endif
@@ -72,6 +48,8 @@
#define FILE_OPEN_EVENT_WAIT_TIME 3000 // ms
+Q_LOGGING_CATEGORY(lcDeprecated, "qt.tools.qml.deprecated")
+
enum QmlApplicationType {
QmlApplicationTypeUnknown
, QmlApplicationTypeCore
@@ -95,10 +73,18 @@ static QQmlApplicationEngine *qae = nullptr;
#if defined(Q_OS_DARWIN) || defined(QT_GUI_LIB)
static int exitTimerId = -1;
#endif
-static const QString iconResourcePath(QStringLiteral(":/qt-project.org/QmlRuntime/resources/qml-64.png"));
-static const QString confResourcePath(QStringLiteral(":/qt-project.org/QmlRuntime/conf/"));
+static const QString iconResourcePath(QStringLiteral(":/qt-project.org/imports/QmlRuntime/Config/resources/qml-64.png"));
+static const QString confResourcePath(QStringLiteral(":/qt-project.org/imports/QmlRuntime/Config/"));
+static const QString customConfFileName(QStringLiteral("configuration.qml"));
static bool verboseMode = false;
static bool quietMode = false;
+static bool glShareContexts = true;
+static bool disableShaderCache = true;
+#if defined(QT_GUI_LIB)
+static bool requestAlphaChannel = false;
+static bool requestMSAA = false;
+static bool requestCoreProfile = false;
+#endif
static void loadConf(const QString &override, bool quiet) // Terminates app on failure
{
@@ -109,27 +95,31 @@ static void loadConf(const QString &override, bool quiet) // Terminates app on f
QFileInfo fi;
fi.setFile(QStandardPaths::locate(QStandardPaths::AppDataLocation, defaultFileName));
if (fi.exists()) {
- settingsUrl = QUrl::fromLocalFile(fi.absoluteFilePath());
+ settingsUrl = QQmlImports::urlFromLocalFileOrQrcOrUrl(fi.absoluteFilePath());
} else {
// ### If different built-in configs are needed per-platform, just apply QFileSelector to the qrc conf.qml path
fi.setFile(confResourcePath + defaultFileName);
- settingsUrl = QUrl::fromLocalFile(fi.absoluteFilePath());
+ settingsUrl = QQmlImports::urlFromLocalFileOrQrcOrUrl(fi.absoluteFilePath());
builtIn = true;
}
} else {
QFileInfo fi;
fi.setFile(confResourcePath + override + QLatin1String(".qml"));
if (fi.exists()) {
- settingsUrl = QUrl::fromLocalFile(fi.absoluteFilePath());
+ settingsUrl = QQmlImports::urlFromLocalFileOrQrcOrUrl(fi.absoluteFilePath());
builtIn = true;
} else {
- fi.setFile(override);
+ fi.setFile(QDir(QStandardPaths::locate(QStandardPaths::AppConfigLocation, override, QStandardPaths::LocateDirectory)), customConfFileName);
+ if (fi.exists())
+ settingsUrl = QQmlImports::urlFromLocalFileOrQrcOrUrl(fi.absoluteFilePath());
+ else
+ fi.setFile(override);
if (!fi.exists()) {
printf("qml: Couldn't find required configuration file: %s\n",
qPrintable(QDir::toNativeSeparators(fi.absoluteFilePath())));
exit(1);
}
- settingsUrl = QUrl::fromLocalFile(fi.absoluteFilePath());
+ settingsUrl = QQmlImports::urlFromLocalFileOrQrcOrUrl(fi.absoluteFilePath());
}
}
@@ -166,10 +156,38 @@ void noFilesGiven()
static void listConfFiles()
{
- QDir confResourceDir(confResourcePath);
+ const QDir confResourceDir(confResourcePath);
printf("%s\n", qPrintable(QCoreApplication::translate("main", "Built-in configurations:")));
- for (const QFileInfo &fi : confResourceDir.entryInfoList(QDir::Files))
- printf(" %s\n", qPrintable(fi.baseName()));
+ for (const QFileInfo &fi : confResourceDir.entryInfoList(QDir::Files)) {
+ if (fi.completeSuffix() != QLatin1String("qml"))
+ continue;
+
+ const QString baseName = fi.baseName();
+ if (baseName.isEmpty() || baseName[0].isUpper())
+ continue;
+
+ printf(" %s\n", qPrintable(baseName));
+ }
+ printf("%s\n", qPrintable(QCoreApplication::translate("main", "Other configurations:")));
+ bool foundOther = false;
+ const QStringList otherLocations = QStandardPaths::standardLocations(QStandardPaths::AppConfigLocation);
+ for (const auto &confDirPath : otherLocations) {
+ const QDir confDir(confDirPath);
+ for (const QFileInfo &fi : confDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot)) {
+ foundOther = true;
+ if (verboseMode)
+ printf(" %s\n", qPrintable(fi.absoluteFilePath()));
+ else
+ printf(" %s\n", qPrintable(fi.baseName()));
+ }
+ }
+ if (!foundOther)
+ printf(" %s\n", qPrintable(QCoreApplication::translate("main", "none")));
+ if (verboseMode) {
+ printf("%s\n", qPrintable(QCoreApplication::translate("main", "Checked in:")));
+ for (const auto &confDirPath : otherLocations)
+ printf(" %s\n", qPrintable(confDirPath));
+ }
exit(0);
}
@@ -230,18 +248,17 @@ public Q_SLOTS:
{
Q_UNUSED(url);
if (o) {
- checkForWindow(o);
+ ++createdObjects;
if (conf && qae)
- for (PartialScene *ps : qAsConst(conf->completers))
+ for (PartialScene *ps : std::as_const(conf->completers))
if (o->inherits(ps->itemType().toUtf8().constData()))
contain(o, ps->container());
}
- if (haveWindow)
- return;
- if (! --expectedFileCount) {
+ if (!--expectedFileCount && !createdObjects) {
printf("qml: Did not load any objects, exiting.\n");
- std::exit(2); // Different return code from qFatal
+ exit(2);
+ QCoreApplication::exit(2);
}
}
@@ -257,11 +274,10 @@ public Q_SLOTS:
private:
void contain(QObject *o, const QUrl &containPath);
- void checkForWindow(QObject *o);
private:
- bool haveWindow = false;
int expectedFileCount;
+ int createdObjects = 0;
};
void LoadWatcher::contain(QObject *o, const QUrl &containPath)
@@ -270,7 +286,7 @@ void LoadWatcher::contain(QObject *o, const QUrl &containPath)
QObject *o2 = c.create();
if (!o2)
return;
- checkForWindow(o2);
+ o2->setParent(this);
bool success = false;
int idx;
if ((idx = o2->metaObject()->indexOfProperty("containedObject")) != -1)
@@ -279,16 +295,6 @@ void LoadWatcher::contain(QObject *o, const QUrl &containPath)
o->setParent(o2); // Set QObject parent, and assume container will react as needed
}
-void LoadWatcher::checkForWindow(QObject *o)
-{
-#if defined(QT_GUI_LIB)
- if (o->isWindowType() && o->inherits("QQuickWindow"))
- haveWindow = true;
-#else
- Q_UNUSED(o);
-#endif // QT_GUI_LIB
-}
-
void quietMessageHandler(QtMsgType type, const QMessageLogContext &ctxt, const QString &msg)
{
Q_UNUSED(ctxt);
@@ -330,6 +336,16 @@ static void getAppFlags(int argc, char **argv)
QCoreApplication::setAttribute(Qt::AA_UseOpenGLES);
} else if (!strcmp(argv[i], "-software") || !strcmp(argv[i], "--software")) {
QCoreApplication::setAttribute(Qt::AA_UseSoftwareOpenGL);
+ } else if (!strcmp(argv[i], "-disable-context-sharing") || !strcmp(argv[i], "--disable-context-sharing")) {
+ glShareContexts = false;
+ } else if (!strcmp(argv[i], "-enable-shader-cache") || !strcmp(argv[i], "--enable-shader-cache")) {
+ disableShaderCache = false;
+ } else if (!strcmp(argv[i], "-transparent") || !strcmp(argv[i], "--transparent")) {
+ requestAlphaChannel = true;
+ } else if (!strcmp(argv[i], "-multisample") || !strcmp(argv[i], "--multisample")) {
+ requestMSAA = true;
+ } else if (!strcmp(argv[i], "-core-profile") || !strcmp(argv[i], "--core-profile")) {
+ requestCoreProfile = true;
}
}
#else
@@ -338,6 +354,7 @@ static void getAppFlags(int argc, char **argv)
#endif // QT_GUI_LIB
}
+#if QT_DEPRECATED_SINCE(6, 3)
static void loadDummyDataFiles(QQmlEngine &engine, const QString& directory)
{
QDir dir(directory+"/dummydata", "*.qml");
@@ -355,16 +372,44 @@ static void loadDummyDataFiles(QQmlEngine &engine, const QString& directory)
if (dummyData && !quietMode) {
printf("qml: Loaded dummy data: %s\n", qPrintable(dir.filePath(qml)));
- qml.truncate(qml.length()-4);
+ qml.truncate(qml.size()-4);
engine.rootContext()->setContextProperty(qml, dummyData);
dummyData->setParent(&engine);
}
}
}
+#endif
int main(int argc, char *argv[])
{
getAppFlags(argc, argv);
+
+ // Must set the default QSurfaceFormat before creating the app object if
+ // AA_ShareOpenGLContexts is going to be set.
+#if defined(QT_GUI_LIB)
+ QSurfaceFormat surfaceFormat;
+ surfaceFormat.setDepthBufferSize(24);
+ surfaceFormat.setStencilBufferSize(8);
+ if (requestMSAA)
+ surfaceFormat.setSamples(4);
+ if (requestAlphaChannel)
+ surfaceFormat.setAlphaBufferSize(8);
+ if (qEnvironmentVariableIsSet("QSG_CORE_PROFILE")
+ || qEnvironmentVariableIsSet("QML_CORE_PROFILE")
+ || requestCoreProfile)
+ {
+ // intentionally requesting 4.1 core to play nice with macOS
+ surfaceFormat.setVersion(4, 1);
+ surfaceFormat.setProfile(QSurfaceFormat::CoreProfile);
+ }
+ QSurfaceFormat::setDefaultFormat(surfaceFormat);
+#endif
+
+ if (glShareContexts)
+ QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
+ if (disableShaderCache)
+ QCoreApplication::setAttribute(Qt::AA_DisableShaderDiskCache);
+
std::unique_ptr<QCoreApplication> app;
switch (applicationType) {
#ifdef QT_GUI_LIB
@@ -390,18 +435,16 @@ int main(int argc, char *argv[])
app->setOrganizationDomain("qt-project.org");
QCoreApplication::setApplicationVersion(QLatin1String(QT_VERSION_STR));
- QQmlApplicationEngine e;
QStringList files;
QString confFile;
QString translationFile;
- QString dummyDir;
// Handle main arguments
QCommandLineParser parser;
parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
parser.setOptionsAfterPositionalArgumentsMode(QCommandLineParser::ParseAsPositionalArguments);
- const QCommandLineOption helpOption = parser.addHelpOption();
- const QCommandLineOption versionOption = parser.addVersionOption();
+ parser.addHelpOption();
+ parser.addVersionOption();
#ifdef QT_GUI_LIB
QCommandLineOption apptypeOption(QStringList() << QStringLiteral("a") << QStringLiteral("apptype"),
QCoreApplication::translate("main", "Select which application class to use. Default is gui."),
@@ -427,9 +470,11 @@ int main(int argc, char *argv[])
QCommandLineOption translationOption(QStringLiteral("translation"),
QCoreApplication::translate("main", "Load the given file as the translations file."), QStringLiteral("file"));
parser.addOption(translationOption);
+#if QT_DEPRECATED_SINCE(6, 3)
QCommandLineOption dummyDataOption(QStringLiteral("dummy-data"),
- QCoreApplication::translate("main", "Load QML files from the given directory as context properties."), QStringLiteral("file"));
+ QCoreApplication::translate("main", "Load QML files from the given directory as context properties. (deprecated)"), QStringLiteral("file"));
parser.addOption(dummyDataOption);
+#endif
#ifdef QT_GUI_LIB
// OpenGL options
QCommandLineOption glDesktopOption(QStringLiteral("desktop"),
@@ -441,7 +486,24 @@ int main(int argc, char *argv[])
QCommandLineOption glSoftwareOption(QStringLiteral("software"),
QCoreApplication::translate("main", "Force use of software rendering (AA_UseSoftwareOpenGL)."));
parser.addOption(glSoftwareOption); // Just for the help text... we've already handled this argument above
+ QCommandLineOption glCoreProfile(QStringLiteral("core-profile"),
+ QCoreApplication::translate("main", "Force use of OpenGL Core Profile."));
+ parser.addOption(glCoreProfile); // Just for the help text... we've already handled this argument above
+ QCommandLineOption glContextSharing(QStringLiteral("disable-context-sharing"),
+ QCoreApplication::translate("main", "Disable the use of a shared GL context for QtQuick Windows"));
+ parser.addOption(glContextSharing); // Just for the help text... we've already handled this argument above
+ // Options relevant for other 3D APIs as well
+ QCommandLineOption shaderCaching(QStringLiteral("enable-shader-cache"),
+ QCoreApplication::translate("main", "Enable persistent caching of generated shaders"));
+ parser.addOption(shaderCaching); // Just for the help text... we've already handled this argument above
+ QCommandLineOption transparentOption(QStringLiteral("transparent"),
+ QCoreApplication::translate("main", "Requests an alpha channel in order to enable semi-transparent windows."));
+ parser.addOption(transparentOption); // Just for the help text... we've already handled this argument above
+ QCommandLineOption multisampleOption(QStringLiteral("multisample"),
+ QCoreApplication::translate("main", "Requests 4x multisample antialiasing."));
+ parser.addOption(multisampleOption); // Just for the help text... we've already handled this argument above
#endif // QT_GUI_LIB
+
// Debugging and verbosity options
QCommandLineOption quietOption(QStringLiteral("quiet"),
QCoreApplication::translate("main", "Suppress all output."));
@@ -457,7 +519,7 @@ int main(int argc, char *argv[])
parser.addOption(fixedAnimationsOption);
QCommandLineOption rhiOption(QStringList() << QStringLiteral("r") << QStringLiteral("rhi"),
QCoreApplication::translate("main", "Set the backend for the Qt graphics abstraction (RHI). "
- "Backend is one of: default, vulkan, metal, d3d11, gl"),
+ "Backend is one of: default, vulkan, metal, d3d11, d3d12, opengl"),
QStringLiteral("backend"));
parser.addOption(rhiOption);
QCommandLineOption selectorOption(QStringLiteral("S"), QCoreApplication::translate("main",
@@ -470,14 +532,13 @@ int main(int argc, char *argv[])
parser.addPositionalArgument("args",
QCoreApplication::translate("main", "Arguments after '--' are ignored, but passed through to the application.arguments variable in QML."), "[-- args...]");
- if (!parser.parse(QCoreApplication::arguments())) {
- qWarning() << parser.errorText();
- exit(1);
+ parser.process(*app);
+ if (parser.isSet(verboseOption))
+ verboseMode = true;
+ if (parser.isSet(quietOption)) {
+ quietMode = true;
+ verboseMode = false;
}
- if (parser.isSet(versionOption))
- parser.showVersion();
- if (parser.isSet(helpOption))
- parser.showHelp();
if (parser.isSet(listConfOption))
listConfFiles();
if (applicationType == QmlApplicationTypeUnknown) {
@@ -488,18 +549,15 @@ int main(int argc, char *argv[])
#endif // QT_WIDGETS_LIB
parser.showHelp();
}
- if (parser.isSet(verboseOption))
- verboseMode = true;
- if (parser.isSet(quietOption)) {
- quietMode = true;
- verboseMode = false;
- }
#if QT_CONFIG(qml_animation)
if (parser.isSet(slowAnimationsOption))
QUnifiedTimer::instance()->setSlowModeEnabled(true);
if (parser.isSet(fixedAnimationsOption))
QUnifiedTimer::instance()->setConsistentTiming(true);
#endif
+
+ QQmlApplicationEngine e;
+
for (const QString &importPath : parser.values(importOption))
e.addImportPath(importPath);
@@ -510,24 +568,11 @@ int main(int argc, char *argv[])
if (!customSelectors.isEmpty())
e.setExtraFileSelectors(customSelectors);
-#if defined(QT_GUI_LIB)
- if (qEnvironmentVariableIsSet("QSG_CORE_PROFILE") || qEnvironmentVariableIsSet("QML_CORE_PROFILE")) {
- QSurfaceFormat surfaceFormat;
- surfaceFormat.setStencilBufferSize(8);
- surfaceFormat.setDepthBufferSize(24);
- surfaceFormat.setVersion(4, 1);
- surfaceFormat.setProfile(QSurfaceFormat::CoreProfile);
- QSurfaceFormat::setDefaultFormat(surfaceFormat);
- }
-#endif
-
files << parser.values(qmlFileOption);
if (parser.isSet(configOption))
confFile = parser.value(configOption);
if (parser.isSet(translationOption))
translationFile = parser.value(translationOption);
- if (parser.isSet(dummyDataOption))
- dummyDir = parser.value(dummyDataOption);
if (parser.isSet(rhiOption)) {
const QString rhiBackend = parser.value(rhiOption);
if (rhiBackend == QLatin1String("default"))
@@ -545,9 +590,8 @@ int main(int argc, char *argv[])
#if QT_CONFIG(translation)
// Need to be installed before QQmlApplicationEngine's automatic translation loading
// (qt_ translations are loaded there)
+ QTranslator translator;
if (!translationFile.isEmpty()) {
- QTranslator translator;
-
if (translator.load(translationFile)) {
app->installTranslator(&translator);
if (verboseMode)
@@ -567,7 +611,7 @@ int main(int argc, char *argv[])
QLoggingCategory::setFilterRules(QStringLiteral("*=false"));
}
- if (files.count() <= 0) {
+ if (files.size() <= 0) {
#if defined(Q_OS_DARWIN) && defined(QT_GUI_LIB)
if (applicationType == QmlApplicationTypeGui)
exitTimerId = static_cast<LoaderApplication *>(app.get())->startTimer(FILE_OPEN_EVENT_WAIT_TIME);
@@ -580,13 +624,20 @@ int main(int argc, char *argv[])
loadConf(confFile, !verboseMode);
// Load files
- QScopedPointer<LoadWatcher> lw(new LoadWatcher(&e, files.count()));
+ QScopedPointer<LoadWatcher> lw(new LoadWatcher(&e, files.size()));
+#if QT_DEPRECATED_SINCE(6, 3)
+ QString dummyDir;
+ if (parser.isSet(dummyDataOption))
+ dummyDir = parser.value(dummyDataOption);
// Load dummy data before loading QML-files
- if (!dummyDir.isEmpty() && QFileInfo (dummyDir).isDir())
+ if (!dummyDir.isEmpty() && QFileInfo (dummyDir).isDir()) {
+ qCWarning(lcDeprecated()) << "Warning: the qml --dummy-data option is deprecated and will be removed in a future version of Qt.";
loadDummyDataFiles(e, dummyDir);
+ }
+#endif
- for (const QString &path : qAsConst(files)) {
+ for (const QString &path : std::as_const(files)) {
QUrl url = QUrl::fromUserInput(path, QDir::currentPath(), QUrl::AssumeLocalFile);
if (verboseMode)
printf("qml: loading %s\n", qPrintable(url.toString()));
diff --git a/tools/qml/qml.qrc b/tools/qml/qml.qrc
deleted file mode 100644
index 69aa4a5756..0000000000
--- a/tools/qml/qml.qrc
+++ /dev/null
@@ -1,9 +0,0 @@
-<RCC>
- <qresource prefix="qt-project.org/QmlRuntime">
- <file>conf/default.qml</file>
- <file>conf/resizeToItem.qml</file>
- <file>conf/content/resizeItemToWindow.qml</file>
- <file>conf/content/resizeWindowToItem.qml</file>
- <file>resources/qml-64.png</file>
- </qresource>
-</RCC>
diff --git a/tools/qml/resizeToItem.qml b/tools/qml/resizeToItem.qml
new file mode 100644
index 0000000000..5bddd8ebaa
--- /dev/null
+++ b/tools/qml/resizeToItem.qml
@@ -0,0 +1,10 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+import QmlRuntime.Config
+
+Configuration {
+ PartialScene {
+ itemType: "QQuickItem"
+ container: "qrc:/qt-project.org/imports/QmlRuntime/Config/ResizeWindowToItem.qml"
+ }
+}
diff --git a/tools/qmlcachegen/.prev_CMakeLists.txt b/tools/qmlcachegen/.prev_CMakeLists.txt
deleted file mode 100644
index a33e080bbe..0000000000
--- a/tools/qmlcachegen/.prev_CMakeLists.txt
+++ /dev/null
@@ -1,46 +0,0 @@
-# Generated from qmlcachegen.pro.
-
-#####################################################################
-## qmlcachegen Tool:
-#####################################################################
-
-qt_get_tool_target_name(target_name qmlcachegen)
-qt_internal_add_tool(${target_name}
- TARGET_DESCRIPTION "QML Cache Generator"
- SOURCES
- qmlcachegen.cpp
- DEFINES
- QT_NO_CAST_FROM_ASCII
- QT_NO_CAST_TO_ASCII
- PUBLIC_LIBRARIES
- Qt::QmlCompilerPrivate
- Qt::QmlDevToolsPrivate
-)
-
-#### Keys ignored in scope 1:.:.:qmlcachegen.pro:<TRUE>:
-# CMAKE_BIN_DIR = "$$cmakeRelativePath($$[QT_HOST_BINS], $$[QT_INSTALL_PREFIX])"
-# QMAKE_SUBSTITUTES = "cmake_config_file"
-# QMAKE_TARGET_DESCRIPTION = "QML" "Cache" "Generator"
-# _OPTION = "host_build"
-# build_integration.files = "qmlcache.prf" "qtquickcompiler.prf"
-# build_integration.path = "$$[QT_HOST_DATA]/mkspecs/features"
-# cmake_build_integration.files = "$$cmake_config_file.output"
-# cmake_build_integration.path = "$$[QT_INSTALL_LIBS]/cmake/Qt6QuickCompiler"
-# cmake_config_file.input = "$$PWD/Qt6QuickCompilerConfig.cmake.in"
-# cmake_config_file.output = "$$MODULE_BASE_OUTDIR/lib/cmake/Qt6QuickCompiler/Qt6QuickCompilerConfig.cmake"
-
-## Scopes:
-#####################################################################
-
-#### Keys ignored in scope 2:.:.:qmlcachegen.pro:prefix_build:
-# INSTALLS = "cmake_build_integration" "build_integration"
-
-#### Keys ignored in scope 3:.:.:qmlcachegen.pro:else:
-# COPIES = "cmake_build_integration" "build_integration"
-
-#### Keys ignored in scope 4:.:.:qmlcachegen.pro:CMAKE_BIN_DIR___contains___^\\.\\./._x_:
-# CMAKE_BIN_DIR = "$$[QT_HOST_BINS]/"
-# CMAKE_BIN_DIR_IS_ABSOLUTE = "True"
-
-#### Keys ignored in scope 5:.:.:qmlcachegen.pro:QMAKE_HOST.os___equals___Windows:
-# CMAKE_BIN_SUFFIX = ".exe"
diff --git a/tools/qmlcachegen/CMakeLists.txt b/tools/qmlcachegen/CMakeLists.txt
index e0ae4c4092..3f58390f2d 100644
--- a/tools/qmlcachegen/CMakeLists.txt
+++ b/tools/qmlcachegen/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qmlcachegen.pro.
#####################################################################
@@ -8,15 +11,16 @@ qt_get_tool_target_name(target_name qmlcachegen)
qt_internal_add_tool(${target_name}
TARGET_DESCRIPTION "QML Cache Generator"
TOOLS_TARGET Qml # special case
+ INSTALL_DIR "${INSTALL_LIBEXECDIR}"
SOURCES
qmlcachegen.cpp
DEFINES
QT_NO_CAST_FROM_ASCII
QT_NO_CAST_TO_ASCII
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::QmlCompilerPrivate
- Qt::QmlDevToolsPrivate
)
+qt_internal_return_unless_building_tools()
#### Keys ignored in scope 1:.:.:qmlcachegen.pro:<TRUE>:
# CMAKE_BIN_DIR = "$$cmakeRelativePath($$[QT_HOST_BINS], $$[QT_INSTALL_PREFIX])"
diff --git a/tools/qmlcachegen/Qt6QuickCompilerConfig.cmake.in b/tools/qmlcachegen/Qt6QuickCompilerConfig.cmake.in
index fe19af78cb..1ed1568de5 100644
--- a/tools/qmlcachegen/Qt6QuickCompilerConfig.cmake.in
+++ b/tools/qmlcachegen/Qt6QuickCompilerConfig.cmake.in
@@ -1,4 +1,5 @@
-function(QTQUICK_COMPILER_ADD_RESOURCES)
- message(WARNING "Use qt6_add_resources instead of qtquick_compiler_add_resources."
- "QML and JavaScript files are automatically compiled then.")
+function(qtquick_compiler_add_resources)
+ message(WARNING "Use qt_add_qml_module instead of qtquick_compiler_add_resources."
+ "QML and JavaScript files are automatically compiled when creating a QML"
+ " module.")
endfunction()
diff --git a/tools/qmlcachegen/qmlcache.prf b/tools/qmlcachegen/qmlcache.prf
index 537eaf62ea..0f0d291dfd 100644
--- a/tools/qmlcachegen/qmlcache.prf
+++ b/tools/qmlcachegen/qmlcache.prf
@@ -3,7 +3,7 @@ static {
return()
}
-qtPrepareTool(QML_CACHEGEN, qmlcachegen, _ARCH_CHECK)
+qtPrepareLibExecTool(QML_CACHEGEN, qmlcachegen, _ARCH_CHECK)
isEmpty(TARGETPATH): error("Must set TARGETPATH (QML import name) for ahead-of-time QML cache generation")
diff --git a/tools/qmlcachegen/qmlcachegen.cpp b/tools/qmlcachegen/qmlcachegen.cpp
index 6584d76af8..1180931d9f 100644
--- a/tools/qmlcachegen/qmlcachegen.cpp
+++ b/tools/qmlcachegen/qmlcachegen.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QCoreApplication>
#include <QStringList>
@@ -36,6 +11,8 @@
#include <QSaveFile>
#include <QScopedPointer>
#include <QScopeGuard>
+#include <QLibraryInfo>
+#include <QLoggingCategory>
#include <private/qqmlirbuilder_p.h>
#include <private/qqmljsparser_p.h>
@@ -47,14 +24,14 @@
#include <algorithm>
-using namespace QQmlJS;
+using namespace Qt::Literals::StringLiterals;
static bool argumentsFromCommandLineAndFile(QStringList& allArguments, const QStringList &arguments)
{
allArguments.reserve(arguments.size());
for (const QString &argument : arguments) {
// "@file" doesn't start with a '-' so we can't use QCommandLineParser for it
- if (argument.startsWith(QLatin1Char('@'))) {
+ if (argument.startsWith(u'@')) {
QString optionsFile = argument;
optionsFile.remove(0, 1);
if (optionsFile.isEmpty()) {
@@ -81,46 +58,52 @@ static bool argumentsFromCommandLineAndFile(QStringList& allArguments, const QSt
int main(int argc, char **argv)
{
// Produce reliably the same output for the same input by disabling QHash's random seeding.
- qSetGlobalQHashSeed(0);
+ QHashSeed::setDeterministicGlobalSeed();
QCoreApplication app(argc, argv);
- QCoreApplication::setApplicationName(QStringLiteral("qmlcachegen"));
+ QCoreApplication::setApplicationName("qmlcachegen"_L1);
QCoreApplication::setApplicationVersion(QLatin1String(QT_VERSION_STR));
QCommandLineParser parser;
parser.addHelpOption();
parser.addVersionOption();
- QCommandLineOption filterResourceFileOption(QStringLiteral("filter-resource-file"), QCoreApplication::translate("main", "Filter out QML/JS files from a resource file that can be cached ahead of time instead"));
+ QCommandLineOption bareOption("bare"_L1, QCoreApplication::translate("main", "Do not include default import directories. This may be used to run qmlcachegen on a project using a different Qt version."));
+ parser.addOption(bareOption);
+ QCommandLineOption filterResourceFileOption("filter-resource-file"_L1, QCoreApplication::translate("main", "Filter out QML/JS files from a resource file that can be cached ahead of time instead"));
parser.addOption(filterResourceFileOption);
- QCommandLineOption resourceFileMappingOption(QStringLiteral("resource-file-mapping"), QCoreApplication::translate("main", "Path from original resource file to new one"), QCoreApplication::translate("main", "old-name:new-name"));
+ QCommandLineOption resourceFileMappingOption("resource-file-mapping"_L1, QCoreApplication::translate("main", "Path from original resource file to new one"), QCoreApplication::translate("main", "old-name=new-name"));
parser.addOption(resourceFileMappingOption);
- QCommandLineOption resourceOption(QStringLiteral("resource"), QCoreApplication::translate("main", "Qt resource file that might later contain one of the compiled files"), QCoreApplication::translate("main", "resource-file-name"));
+ QCommandLineOption resourceOption("resource"_L1, QCoreApplication::translate("main", "Qt resource file that might later contain one of the compiled files"), QCoreApplication::translate("main", "resource-file-name"));
parser.addOption(resourceOption);
- QCommandLineOption resourcePathOption(QStringLiteral("resource-path"), QCoreApplication::translate("main", "Qt resource file path corresponding to the file being compiled"), QCoreApplication::translate("main", "resource-path"));
+ QCommandLineOption resourcePathOption("resource-path"_L1, QCoreApplication::translate("main", "Qt resource file path corresponding to the file being compiled"), QCoreApplication::translate("main", "resource-path"));
parser.addOption(resourcePathOption);
- QCommandLineOption resourceNameOption(QStringLiteral("resource-name"),
- QCoreApplication::translate("main", "Required to generate qmlcache_loader without qrc files. This is the name of the Qt resource the input files belong to."),
- QCoreApplication::translate("main", "compiled-file-list"));
+ QCommandLineOption resourceNameOption("resource-name"_L1, QCoreApplication::translate("main", "Required to generate qmlcache_loader without qrc files. This is the name of the Qt resource the input files belong to."), QCoreApplication::translate("main", "compiled-file-list"));
parser.addOption(resourceNameOption);
- QCommandLineOption directCallsOption(QStringLiteral("direct-calls"), QCoreApplication::translate("main", "This option is ignored."));
+ QCommandLineOption directCallsOption("direct-calls"_L1, QCoreApplication::translate("main", "This option is ignored."));
directCallsOption.setFlags(QCommandLineOption::HiddenFromHelp);
parser.addOption(directCallsOption);
- QCommandLineOption qmlJSRuntimeOption(QStringLiteral("qmljs-runtime"), QCoreApplication::translate("main", "This option is ignored."));
- qmlJSRuntimeOption.setFlags(QCommandLineOption::HiddenFromHelp);
- parser.addOption(qmlJSRuntimeOption);
- QCommandLineOption includesOption(QStringLiteral("i"), QCoreApplication::translate("main", "This option is ignored."), QCoreApplication::translate("main", "ignored file"));
- includesOption.setFlags(QCommandLineOption::HiddenFromHelp);
- parser.addOption(includesOption);
- QCommandLineOption importPathOption(QStringLiteral("I"), QCoreApplication::translate("main", "This option is ignored."), QCoreApplication::translate("main", "ignored path"));
- importPathOption.setFlags(QCommandLineOption::HiddenFromHelp);
+ QCommandLineOption staticOption("static"_L1, QCoreApplication::translate("main", "This option is ignored."));
+ staticOption.setFlags(QCommandLineOption::HiddenFromHelp);
+ parser.addOption(staticOption);
+ QCommandLineOption importsOption("i"_L1, QCoreApplication::translate("main", "Import extra qmldir"), QCoreApplication::translate("main", "qmldir file"));
+ parser.addOption(importsOption);
+ QCommandLineOption importPathOption("I"_L1, QCoreApplication::translate("main", "Look for QML modules in specified directory"), QCoreApplication::translate("main", "import directory"));
parser.addOption(importPathOption);
+ QCommandLineOption onlyBytecode("only-bytecode"_L1, QCoreApplication::translate("main", "Generate only byte code for bindings and functions, no C++ code"));
+ parser.addOption(onlyBytecode);
+ QCommandLineOption verboseOption("verbose"_L1, QCoreApplication::translate("main", "Output compile warnings"));
+ parser.addOption(verboseOption);
+ QCommandLineOption warningsAreErrorsOption("warnings-are-errors"_L1, QCoreApplication::translate("main", "Treat warnings as errors"));
+ parser.addOption(warningsAreErrorsOption);
- QCommandLineOption outputFileOption(QStringLiteral("o"), QCoreApplication::translate("main", "Output file name"), QCoreApplication::translate("main", "file name"));
+ QCommandLineOption validateBasicBlocksOption("validate-basic-blocks"_L1, QCoreApplication::translate("main", "Performs checks on the basic blocks of a function compiled ahead of time to validate its structure and coherence"));
+ parser.addOption(validateBasicBlocksOption);
+
+ QCommandLineOption outputFileOption("o"_L1, QCoreApplication::translate("main", "Output file name"), QCoreApplication::translate("main", "file name"));
parser.addOption(outputFileOption);
- parser.addPositionalArgument(QStringLiteral("[qml file]"),
- QStringLiteral("QML source file to generate cache for."));
+ parser.addPositionalArgument("[qml file]"_L1, "QML source file to generate cache for."_L1);
parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
@@ -142,9 +125,9 @@ int main(int argc, char **argv)
if (parser.isSet(outputFileOption))
outputFileName = parser.value(outputFileOption);
- if (outputFileName.endsWith(QLatin1String(".cpp"))) {
+ if (outputFileName.endsWith(".cpp"_L1)) {
target = GenerateCpp;
- if (outputFileName.endsWith(QLatin1String("qmlcache_loader.cpp")))
+ if (outputFileName.endsWith("qmlcache_loader.cpp"_L1))
target = GenerateLoader;
}
@@ -154,14 +137,14 @@ int main(int argc, char **argv)
const QStringList sources = parser.positionalArguments();
if (sources.isEmpty()){
parser.showHelp();
- } else if (sources.count() > 1 && (target != GenerateLoader && target != GenerateLoaderStandAlone)) {
- fprintf(stderr, "%s\n", qPrintable(QStringLiteral("Too many input files specified: '") + sources.join(QStringLiteral("' '")) + QLatin1Char('\'')));
+ } else if (sources.size() > 1 && (target != GenerateLoader && target != GenerateLoaderStandAlone)) {
+ fprintf(stderr, "%s\n", qPrintable("Too many input files specified: '"_L1 + sources.join("' '"_L1) + u'\''));
return EXIT_FAILURE;
}
const QString inputFile = !sources.isEmpty() ? sources.first() : QString();
if (outputFileName.isEmpty())
- outputFileName = inputFile + QLatin1Char('c');
+ outputFileName = inputFile + u'c';
if (parser.isSet(filterResourceFileOption))
return qRelocateResourceFile(inputFile, outputFileName);
@@ -173,7 +156,7 @@ int main(int argc, char **argv)
if (!qQmlJSGenerateLoader(
mapper.resourcePaths(QQmlJSResourceFileMapper::allQmlJSFilter()),
outputFileName, parser.values(resourceFileMappingOption), &error.message)) {
- error.augment(QLatin1String("Error generating loader stub: ")).print();
+ error.augment("Error generating loader stub: "_L1).print();
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
@@ -183,7 +166,7 @@ int main(int argc, char **argv)
QQmlJSCompileError error;
if (!qQmlJSGenerateLoader(sources, outputFileName,
parser.values(resourceNameOption), &error.message)) {
- error.augment(QLatin1String("Error generating loader stub: ")).print();
+ error.augment("Error generating loader stub: "_L1).print();
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
@@ -191,41 +174,39 @@ int main(int argc, char **argv)
QString inputFileUrl = inputFile;
QQmlJSSaveFunction saveFunction;
- if (target == GenerateCpp) {
- QQmlJSResourceFileMapper fileMapper(parser.values(resourceOption));
- QString inputResourcePath = parser.value(resourcePathOption);
-
- // If the user didn't specify the resource path corresponding to the file on disk being
- // compiled, try to determine it from the resource file, if one was supplied.
- if (inputResourcePath.isEmpty()) {
- const QStringList resourcePaths = fileMapper.resourcePaths(
- QQmlJSResourceFileMapper::localFileFilter(inputFile));
- if (resourcePaths.isEmpty()) {
- fprintf(stderr, "No resource path for file: %s\n", qPrintable(inputFile));
- return EXIT_FAILURE;
- }
-
- if (resourcePaths.size() != 1) {
- fprintf(stderr, "Multiple resource paths for file %s. "
- "Use the --%s option to disambiguate:\n",
- qPrintable(inputFile),
- qPrintable(resourcePathOption.names().first()));
- for (const QString &resourcePath: resourcePaths)
- fprintf(stderr, "\t%s\n", qPrintable(resourcePath));
- return EXIT_FAILURE;
- }
+ QQmlJSResourceFileMapper fileMapper(parser.values(resourceOption));
+ QString inputResourcePath = parser.value(resourcePathOption);
+
+ // If the user didn't specify the resource path corresponding to the file on disk being
+ // compiled, try to determine it from the resource file, if one was supplied.
+ if (inputResourcePath.isEmpty()) {
+ const QStringList resourcePaths = fileMapper.resourcePaths(
+ QQmlJSResourceFileMapper::localFileFilter(inputFile));
+ if (target == GenerateCpp && resourcePaths.isEmpty()) {
+ fprintf(stderr, "No resource path for file: %s\n", qPrintable(inputFile));
+ return EXIT_FAILURE;
+ }
+ if (resourcePaths.size() == 1) {
inputResourcePath = resourcePaths.first();
+ } else if (target == GenerateCpp) {
+ fprintf(stderr, "Multiple resource paths for file %s. "
+ "Use the --%s option to disambiguate:\n",
+ qPrintable(inputFile),
+ qPrintable(resourcePathOption.names().first()));
+ for (const QString &resourcePath: resourcePaths)
+ fprintf(stderr, "\t%s\n", qPrintable(resourcePath));
+ return EXIT_FAILURE;
}
+ }
- inputFileUrl = QStringLiteral("qrc://") + inputResourcePath;
-
+ if (target == GenerateCpp) {
+ inputFileUrl = "qrc://"_L1 + inputResourcePath;
saveFunction = [inputResourcePath, outputFileName](
const QV4::CompiledData::SaveableUnitPointer &unit,
const QQmlJSAotFunctionMap &aotFunctions,
QString *errorString) {
- return qSaveQmlJSUnitAsCpp(inputResourcePath, outputFileName, unit, aotFunctions,
- errorString);
+ return qSaveQmlJSUnitAsCpp(inputResourcePath, outputFileName, unit, aotFunctions, errorString);
};
} else {
@@ -241,20 +222,72 @@ int main(int argc, char **argv)
};
}
- if (inputFile.endsWith(QLatin1String(".qml"))) {
+ if (inputFile.endsWith(".qml"_L1)) {
QQmlJSCompileError error;
- if (!qCompileQmlFile(inputFile, saveFunction, nullptr, &error)) {
- error.augment(QLatin1String("Error compiling qml file: ")).print();
- return EXIT_FAILURE;
+ if (target != GenerateCpp || inputResourcePath.isEmpty() || parser.isSet(onlyBytecode)) {
+ if (!qCompileQmlFile(inputFile, saveFunction, nullptr, &error,
+ /* storeSourceLocation */ false)) {
+ error.augment("Error compiling qml file: "_L1).print();
+ return EXIT_FAILURE;
+ }
+ } else {
+ QStringList importPaths;
+
+ if (parser.isSet(resourceOption)) {
+ importPaths.append("qt-project.org/imports"_L1);
+ importPaths.append("qt/qml"_L1);
+ };
+
+ if (parser.isSet(importPathOption))
+ importPaths.append(parser.values(importPathOption));
+
+ if (!parser.isSet(bareOption))
+ importPaths.append(QLibraryInfo::path(QLibraryInfo::QmlImportsPath));
+
+ QQmlJSImporter importer(
+ importPaths, parser.isSet(resourceOption) ? &fileMapper : nullptr);
+ QQmlJSLogger logger;
+
+ // Always trigger the qFatal() on "pragma Strict" violations.
+ logger.setCategoryLevel(qmlCompiler, QtWarningMsg);
+ logger.setCategoryIgnored(qmlCompiler, false);
+ logger.setCategoryFatal(qmlCompiler, true);
+
+ if (!parser.isSet(verboseOption) && !parser.isSet(warningsAreErrorsOption))
+ logger.setSilent(true);
+
+ QQmlJSAotCompiler cppCodeGen(
+ &importer, u':' + inputResourcePath, parser.values(importsOption), &logger);
+
+ if (parser.isSet(validateBasicBlocksOption))
+ cppCodeGen.m_flags.setFlag(QQmlJSAotCompiler::ValidateBasicBlocks);
+
+ if (!qCompileQmlFile(inputFile, saveFunction, &cppCodeGen, &error,
+ /* storeSourceLocation */ true)) {
+ error.augment("Error compiling qml file: "_L1).print();
+ return EXIT_FAILURE;
+ }
+
+ QList<QQmlJS::DiagnosticMessage> warnings = importer.takeGlobalWarnings();
+
+ if (!warnings.isEmpty()) {
+ logger.log("Type warnings occurred while compiling file:"_L1,
+ qmlImport, QQmlJS::SourceLocation());
+ logger.processMessages(warnings, qmlImport);
+ if (parser.isSet(warningsAreErrorsOption))
+ return EXIT_FAILURE;
+ }
}
- } else if (inputFile.endsWith(QLatin1String(".js")) || inputFile.endsWith(QLatin1String(".mjs"))) {
+ } else if (inputFile.endsWith(".js"_L1) || inputFile.endsWith(".mjs"_L1)) {
QQmlJSCompileError error;
if (!qCompileJSFile(inputFile, inputFileUrl, saveFunction, &error)) {
- error.augment(QLatin1String("Error compiling js file: ")).print();
+ error.augment("Error compiling js file: "_L1).print();
return EXIT_FAILURE;
}
} else {
fprintf(stderr, "Ignoring %s input file as it is not QML source code - maybe remove from QML_FILES?\n", qPrintable(inputFile));
+ if (parser.isSet(warningsAreErrorsOption))
+ return EXIT_FAILURE;
}
return EXIT_SUCCESS;
diff --git a/tools/qmlcachegen/qtquickcompiler.prf b/tools/qmlcachegen/qtquickcompiler.prf
index d1b7a5aad5..b7413a3de3 100644
--- a/tools/qmlcachegen/qtquickcompiler.prf
+++ b/tools/qmlcachegen/qtquickcompiler.prf
@@ -10,8 +10,8 @@ if(qtc_run|lupdate_run): return()
unset(qt_modules)
}
-qtPrepareTool(QML_CACHEGEN, qmlcachegen, _FILTER)
-qtPrepareTool(QMAKE_RCC, rcc, _DEP)
+qtPrepareLibExecTool(QML_CACHEGEN, qmlcachegen, _FILTER)
+qtPrepareLibExecTool(QMAKE_RCC, rcc, _DEP)
isEmpty(QMLCACHE_DIR): QMLCACHE_DIR = .
@@ -77,6 +77,7 @@ for(res, QMLCACHE_RESOURCE_FILES) {
defineReplace(qmlCacheOutputName) {
name = $$absolute_path($$1, $$OUT_PWD)
name = $$relative_path($$name, $$_PRO_FILE_PWD_)
+ contains(name, ^\\.\\..*): name = $$relative_path($$1, $$OUT_PWD)
name = $$replace(name, \\.qml$, _qml)
name = $$replace(name, \\.js$, _js)
name = $$replace(name, \\.mjs$, _mjs)
diff --git a/tools/qmldom/CMakeLists.txt b/tools/qmldom/CMakeLists.txt
new file mode 100644
index 0000000000..86e1a10d29
--- /dev/null
+++ b/tools/qmldom/CMakeLists.txt
@@ -0,0 +1,18 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## qmldom Tool:
+#####################################################################
+
+qt_get_tool_target_name(target_name qmldom)
+qt_internal_add_tool(${target_name}
+ TARGET_DESCRIPTION "QML Dom handler"
+ TOOLS_TARGET Qml # special case
+ SOURCES
+ qmldomtool.cpp
+ LIBRARIES
+ Qt::CorePrivate
+ Qt::QmlDomPrivate
+)
+qt_internal_return_unless_building_tools()
diff --git a/tools/qmldom/qmldomtool.cpp b/tools/qmldom/qmldomtool.cpp
new file mode 100644
index 0000000000..99c81e0a95
--- /dev/null
+++ b/tools/qmldom/qmldomtool.cpp
@@ -0,0 +1,324 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qdir.h>
+#include <QtCore/QTextStream>
+#include <QtCore/QThread>
+
+#include <QtQmlDom/private/qqmldomtop_p.h>
+#include <QtQmlDom/private/qqmldomfilewriter_p.h>
+#include <QtQmlDom/private/qqmldomoutwriter_p.h>
+#include <QtQmlDom/private/qqmldomelements_p.h>
+#include <QtQmlDom/private/qqmldomfieldfilter_p.h>
+#include <QtQmlDom/private/qqmldomastdumper_p.h>
+
+#include <cstdio>
+#include <optional>
+
+#if QT_CONFIG(commandlineparser)
+# include <QtCore/qcommandlineparser.h>
+#endif
+
+#include <QtCore/qlibraryinfo.h>
+using namespace QQmlJS::Dom;
+
+namespace tt {
+Q_NAMESPACE
+
+enum class Dependencies { None, Required };
+Q_ENUM_NS(Dependencies);
+
+};
+using namespace tt;
+
+int main(int argc, char *argv[])
+{
+ FieldFilter filter = FieldFilter::defaultFilter();
+ QCoreApplication a(argc, argv);
+ QCoreApplication::setApplicationName("qmldom");
+ QCoreApplication::setApplicationVersion("1.0");
+#if QT_CONFIG(commandlineparser)
+ QCommandLineParser parser;
+ parser.setApplicationDescription(QLatin1String("QML dom tool"));
+ parser.addHelpOption();
+ parser.addVersionOption();
+
+ QCommandLineOption dumpOption(QStringList() << "d"
+ << "dump",
+ QLatin1String("Dumps the code model"));
+ parser.addOption(dumpOption);
+ QCommandLineOption reformatOption(QStringList() << "r"
+ << "reformat",
+ QLatin1String("reformats the files explicitly passed in"));
+ parser.addOption(reformatOption);
+
+ QCommandLineOption filterOption(
+ QStringList() << "f"
+ << "filter-fields",
+ QLatin1String("commas separated list of fields to filter out. Prepending a field with "
+ "'-' skips the field, with '+' it adds it. The field might be prepended "
+ "by '<type>:' to apply only to elements of that type"
+ "The default filters are ")
+ + filter.describeFieldsFilter(),
+ QLatin1String("fields"));
+ parser.addOption(filterOption);
+
+ QCommandLineOption qmltypesDirsOption(
+ QStringList() << "I"
+ << "qmldirs",
+ QLatin1String("Look for qmltypes files in specified directory"),
+ QLatin1String("directory"));
+ parser.addOption(qmltypesDirsOption);
+
+ QCommandLineOption qmltypesFilesOption(QStringList() << "i"
+ << "qmltypes",
+ QLatin1String("Include the specified qmltypes files"),
+ QLatin1String("qmltypes"));
+ parser.addOption(qmltypesFilesOption);
+
+ QCommandLineOption pathToDumpOption(
+ QStringList() << "path-to-dump",
+ QLatin1String("adds a path to dump. By default the base path of each file is dumped. "
+ "If any path starts with $ ($env for example) then the environment (and "
+ "not the loaded files) is used as basis."),
+ QLatin1String("pathToDump"));
+ parser.addOption(pathToDumpOption);
+
+ QCommandLineOption dependenciesOption(
+ QStringList() << "D"
+ << "dependencies",
+ QLatin1String("Dependencies to load: none, required, reachable"),
+ QLatin1String("dependenciesToLoad"), QLatin1String("none"));
+ parser.addOption(dependenciesOption);
+
+ QCommandLineOption reformatDirOption(
+ QStringList() << "reformat-dir",
+ QLatin1String(
+ "Target directory for the reformatted files, "
+ "if not given the files are reformatted in place (but backup files are kept)"),
+ QLatin1String("reformatDir"));
+ parser.addOption(reformatDirOption);
+
+ QCommandLineOption nBackupsOption(
+ QStringList() << "backups",
+ QLatin1String("Number of backup files to generate (default is 2, the oldest, "
+ "and the last version are kept), "),
+ QLatin1String("nBackups"));
+ parser.addOption(nBackupsOption);
+
+ QCommandLineOption dumpAstOption(QStringList() << "dump-ast",
+ QLatin1String("Dumps the AST of the given QML file."));
+ parser.addOption(dumpAstOption);
+
+ parser.addPositionalArgument(QLatin1String("files"),
+ QLatin1String("list of qml or js files to verify"));
+
+ parser.process(a);
+
+ const auto positionalArguments = parser.positionalArguments();
+ if (positionalArguments.isEmpty()) {
+ parser.showHelp(-1);
+ }
+
+ if (parser.isSet(filterOption)) {
+ qDebug() << "filters: " << parser.values(filterOption);
+ for (const QString &fFields : parser.values(filterOption)) {
+ if (!filter.addFilter(fFields)) {
+ return 1;
+ }
+ }
+ filter.setFiltred();
+ }
+
+ std::optional<DomType> fileType;
+ if (parser.isSet(reformatOption))
+ fileType = DomType::QmlFile;
+
+ Dependencies dep = Dependencies::None;
+ for (const QString &depName : parser.values(dependenciesOption)) {
+ QMetaEnum metaEnum = QMetaEnum::fromType<Dependencies>();
+ bool found = false;
+ for (int i = 0; i < metaEnum.keyCount(); ++i) {
+ if (QLatin1String(metaEnum.key(i)).compare(depName, Qt::CaseInsensitive) == 0) {
+ found = true;
+ dep = Dependencies(metaEnum.value(i));
+ }
+ }
+ if (!found) {
+ QStringList values;
+ for (int i = 0; i < metaEnum.keyCount(); ++i)
+ values.append(QString::fromUtf8(metaEnum.key(i)).toLower());
+ qDebug().noquote() << "Invalid dependencies argument, expected one of "
+ << values.join(QLatin1Char(','));
+ return 1;
+ }
+ }
+
+ int nBackups = 2;
+ if (parser.isSet(nBackupsOption)) {
+ bool intOk;
+ nBackups = parser.value(nBackupsOption).toInt(&intOk);
+ if (!intOk) {
+ qDebug() << "expected an integer giving the number of backups after --backups, not "
+ << parser.value(nBackupsOption);
+ }
+ }
+
+ QList<Path> pathsToDump;
+ for (const QString &pStr : parser.values(pathToDumpOption)) {
+ pathsToDump.append(Path::fromString(pStr));
+ }
+ if (pathsToDump.isEmpty())
+ pathsToDump.append(Path());
+
+ // use host qml import path as a sane default if nothing else has been provided
+ QStringList qmltypeDirs = parser.isSet(qmltypesDirsOption)
+ ? parser.values(qmltypesDirsOption)
+ : QStringList { QLibraryInfo::path(QLibraryInfo::Qml2ImportsPath) };
+
+ if (!parser.isSet(qmltypesFilesOption))
+ qmltypeDirs << ".";
+
+ QStringList qmltypeFiles =
+ parser.isSet(qmltypesFilesOption) ? parser.values(qmltypesFilesOption) : QStringList {};
+#else
+ QStringList qmltypeDirs {};
+ QStringList qmltypeFiles {};
+#endif
+
+ {
+ QDebug dbg = qDebug();
+ dbg << "dirs:\n";
+ for (const QString &d : std::as_const(qmltypeDirs))
+ dbg << " '" << d << "'\n";
+ dbg << "files:\n";
+ for (const QString &f : std::as_const(positionalArguments))
+ dbg << " '" << f << "'\n";
+ dbg << "fieldFilter: " << filter.describeFieldsFilter();
+ dbg << "\n";
+ }
+ DomEnvironment::Options options = DomEnvironment::Option::SingleThreaded;
+ if (dep == Dependencies::None)
+ options = options | DomEnvironment::Option::NoDependencies;
+ std::shared_ptr<DomEnvironment> envPtr(new DomEnvironment(qmltypeDirs, options));
+ DomItem env(envPtr);
+ qDebug() << "will load\n";
+ if (dep != Dependencies::None)
+ envPtr->loadBuiltins();
+ QList<DomItem> loadedFiles(positionalArguments.size());
+ qsizetype iPos = 0;
+ for (const QString &s : std::as_const(positionalArguments)) {
+ envPtr->loadFile(
+ FileToLoad::fromFileSystem(envPtr, s),
+ [&loadedFiles, iPos](Path, const DomItem &, const DomItem &newIt) {
+ loadedFiles[iPos] = newIt;
+ },
+ fileType);
+ }
+ envPtr->loadPendingDependencies();
+ bool hadFailures = false;
+ const qsizetype largestFileSizeToCheck = 32000;
+
+ if (parser.isSet(reformatOption)) {
+ for (auto &qmlFile : loadedFiles) {
+ QString qmlFilePath = qmlFile.canonicalFilePath();
+ if (qmlFile.internalKind() != DomType::QmlFile) {
+ qWarning() << "cannot reformat" << qmlFile.internalKindStr() << "(" << qmlFilePath
+ << ")";
+ continue;
+ }
+ qDebug() << "reformatting" << qmlFilePath;
+ FileWriter fw;
+ LineWriterOptions lwOptions;
+ WriteOutChecks checks = WriteOutCheck::Default;
+ if (std::shared_ptr<QmlFile> qmlFilePtr = qmlFile.ownerAs<QmlFile>())
+ if (qmlFilePtr->code().size() > largestFileSizeToCheck)
+ checks = WriteOutCheck::None;
+ QString target = qmlFilePath;
+ QString rDir = parser.value(reformatDirOption);
+ if (!rDir.isEmpty()) {
+ QFileInfo f(qmlFilePath);
+ QDir d(rDir);
+ target = d.filePath(f.fileName());
+ }
+ auto res = qmlFile.writeOut(target, nBackups, lwOptions, &fw, checks);
+ switch (fw.status) {
+ case FileWriter::Status::ShouldWrite:
+ case FileWriter::Status::SkippedDueToFailure:
+ qWarning() << "failure reformatting " << qmlFilePath;
+ break;
+ case FileWriter::Status::DidWrite:
+ qDebug() << "success";
+ break;
+ case FileWriter::Status::SkippedEqual:
+ qDebug() << "no change";
+ }
+ hadFailures = hadFailures || !res;
+ }
+ } else if (parser.isSet(dumpAstOption)) {
+ if (pathsToDump.size() > 1) {
+ qWarning() << "--dump-ast can only be used with a single file";
+ return 1;
+ }
+ for (auto &fileItem : loadedFiles) {
+ const auto file = fileItem.fileObject().ownerAs<QmlFile>();
+ if (!file) {
+ qWarning() << "cannot dump AST for" << fileItem.canonicalPath();
+ qWarning() << "is it a valid QML file?";
+ continue;
+ }
+ const QString ast =
+ QQmlJS::Dom::astNodeDump(file->ast(), AstDumperOption::DumpNode, 1, 0);
+ QTextStream ts(stdout);
+ ts << ast << Qt::flush;
+ }
+ } else if (parser.isSet(dumpOption) || !parser.isSet(reformatOption)
+ || !parser.isSet(dumpAstOption)) {
+ qDebug() << "will dump\n";
+ QTextStream ts(stdout);
+ auto sink = [&ts](QStringView v) {
+ ts << v; /* ts.flush(); */
+ };
+ qsizetype iPathToDump = 0;
+ bool globalPaths = false;
+ for (const auto &p : pathsToDump)
+ if (p.headKind() == Path::Kind::Root)
+ globalPaths = true;
+ if (globalPaths)
+ loadedFiles = QList<DomItem>({ env });
+ bool dumpDict = pathsToDump.size() > 1 || loadedFiles.size() > 1;
+ if (dumpDict)
+ sink(u"{\n");
+ while (iPathToDump < pathsToDump.size()) {
+ for (auto &fileItem : loadedFiles) {
+ Path p = pathsToDump.at(iPathToDump++ % pathsToDump.size());
+ if (dumpDict) {
+ if (iPathToDump > 1)
+ sink(u",\n");
+ sink(u"\"");
+ if (fileItem.internalKind() != DomType::DomEnvironment) {
+ sinkEscaped(sink, fileItem.canonicalFilePath(),
+ EscapeOptions::NoOuterQuotes);
+ sink(u"/");
+ }
+ sinkEscaped(sink, p.toString(), EscapeOptions::NoOuterQuotes);
+ sink(u"\":\n");
+ }
+ fileItem.path(p).dump(sink, 0, filter);
+ }
+ }
+ if (dumpDict)
+ sink(u"}\n");
+ Qt::endl(ts).flush();
+ }
+ for (int i = 0; i < 100; ++i)
+ QThread::yieldCurrentThread(); // let buggy integrations catch up with the output
+ // return a.exec();
+ return 0;
+}
+
+#include "qmldomtool.moc"
diff --git a/tools/qmleasing/Button.qml b/tools/qmleasing/Button.qml
index 1131e559ab..6dd71b0594 100644
--- a/tools/qmleasing/Button.qml
+++ b/tools/qmleasing/Button.qml
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import QtQuick 2.0
@@ -117,26 +92,16 @@ Item {
name: "hovered"
PropertyChanges {
- target: normalBackground
- opacity: 0
- }
-
- PropertyChanges {
- target: hoveredBackground
- opacity: 1
+ normalBackground.opacity: 0
+ hoveredBackground.opacity: 1
}
},
State {
name: "pressed"
PropertyChanges {
- target: normalBackground
- opacity: 0
- }
-
- PropertyChanges {
- target: pressedBackground
- opacity: 1
+ normalBackground.opacity: 0
+ pressedBackground.opacity: 1
}
}
]
diff --git a/tools/qmleasing/CMakeLists.txt b/tools/qmleasing/CMakeLists.txt
index 7257b7cf6e..7bbb0ce7db 100644
--- a/tools/qmleasing/CMakeLists.txt
+++ b/tools/qmleasing/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qmleasing.pro.
#####################################################################
@@ -13,7 +16,7 @@ qt_internal_add_app(qmleasing
properties.ui
segmentproperties.cpp segmentproperties.h
splineeditor.cpp splineeditor.h
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Gui
Qt::Qml
Qt::Quick
@@ -22,6 +25,8 @@ qt_internal_add_app(qmleasing
uic
)
+set_target_properties(qmleasing PROPERTIES WIN32_EXECUTABLE FALSE)
+
# Resources:
set(resources_resource_files
"Button.qml"
diff --git a/tools/qmleasing/main.cpp b/tools/qmleasing/main.cpp
index 389503507e..59c34fd845 100644
--- a/tools/qmleasing/main.cpp
+++ b/tools/qmleasing/main.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "mainwindow.h"
diff --git a/tools/qmleasing/mainwindow.cpp b/tools/qmleasing/mainwindow.cpp
index 679b4c0b91..f38847d107 100644
--- a/tools/qmleasing/mainwindow.cpp
+++ b/tools/qmleasing/mainwindow.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "mainwindow.h"
#include "splineeditor.h"
@@ -69,7 +44,7 @@ MainWindow::MainWindow(QWidget *parent) :
vboxLayout->addWidget(m_placeholder);
ui_properties.plainTextEdit->setPlainText(splineEditor->generateCode());
- connect(splineEditor, SIGNAL(easingCurveCodeChanged(QString)), ui_properties.plainTextEdit, SLOT(setPlainText(QString)));
+ connect(splineEditor, &SplineEditor::easingCurveCodeChanged, ui_properties.plainTextEdit, &QPlainTextEdit::setPlainText);
quickView.rootContext()->setContextProperty(QLatin1String("spinBox"), ui_properties.spinBox);
@@ -77,7 +52,7 @@ MainWindow::MainWindow(QWidget *parent) :
for (const QString &name : presetNames)
ui_properties.comboBox->addItem(name);
- connect(ui_properties.comboBox, SIGNAL(currentIndexChanged(QString)), splineEditor, SLOT(setPreset(QString)));
+ connect(ui_properties.comboBox, &QComboBox::currentTextChanged, splineEditor, &SplineEditor::setPreset);
splineEditor->setPreset(ui_properties.comboBox->currentText());
@@ -87,7 +62,7 @@ MainWindow::MainWindow(QWidget *parent) :
groupBoxLayout->addWidget(splineEditor->pointListWidget());
m_splineEditor = splineEditor;
- connect(ui_properties.plainTextEdit, SIGNAL(textChanged()), this, SLOT(textEditTextChanged()));
+ connect(ui_properties.plainTextEdit, &QPlainTextEdit::textChanged, this, &MainWindow::textEditTextChanged);
QDialog* importDialog = new QDialog(this);
ui_import.setupUi(importDialog);
@@ -95,8 +70,8 @@ MainWindow::MainWindow(QWidget *parent) :
ui_import.inSlopeEdit->setValidator(new QDoubleValidator(this));
ui_import.outInfluenceEdit->setValidator(new QDoubleValidator(this));
ui_import.outSlopeEdit->setValidator(new QDoubleValidator(this));
- connect(ui_properties.importButton, SIGNAL(clicked()), importDialog, SLOT(show()));
- connect(importDialog, SIGNAL(finished(int)), this, SLOT(importData(int)));
+ connect(ui_properties.importButton, &QPushButton::clicked, importDialog, &QDialog::show);
+ connect(importDialog, &QDialog::finished, this, &MainWindow::importData);
initQml();
}
diff --git a/tools/qmleasing/mainwindow.h b/tools/qmleasing/mainwindow.h
index 7ca4dbfd5c..34e3d6365e 100644
--- a/tools/qmleasing/mainwindow.h
+++ b/tools/qmleasing/mainwindow.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
@@ -40,13 +15,11 @@ class MainWindow : public QMainWindow
{
Q_OBJECT
public:
- explicit MainWindow(QWidget *parent = 0);
+ explicit MainWindow(QWidget *parent = nullptr);
void showQuickView();
-signals:
-
-public slots:
+public Q_SLOTS:
void textEditTextChanged();
void importData(int result);
diff --git a/tools/qmleasing/preview.qml b/tools/qmleasing/preview.qml
index 3591684da5..9aa933efa6 100644
--- a/tools/qmleasing/preview.qml
+++ b/tools/qmleasing/preview.qml
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import QtQuick 2.0
@@ -100,11 +75,12 @@ Item {
name: "moved"
PropertyChanges {
- target: rectangle
- x: 567
- y: 9
- anchors.bottomMargin: 6
- anchors.topMargin: 9
+ rectangle {
+ x: 567
+ y: 9
+ anchors.bottomMargin: 6
+ anchors.topMargin: 9
+ }
}
}
]
diff --git a/tools/qmleasing/resources.qrc b/tools/qmleasing/resources.qrc
deleted file mode 100644
index c184af4662..0000000000
--- a/tools/qmleasing/resources.qrc
+++ /dev/null
@@ -1,6 +0,0 @@
-<RCC>
- <qresource prefix="/">
- <file>preview.qml</file>
- <file>Button.qml</file>
- </qresource>
-</RCC>
diff --git a/tools/qmleasing/segmentproperties.cpp b/tools/qmleasing/segmentproperties.cpp
index c61feef9a4..562a241abb 100644
--- a/tools/qmleasing/segmentproperties.cpp
+++ b/tools/qmleasing/segmentproperties.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "segmentproperties.h"
#include "splineeditor.h"
@@ -43,8 +18,8 @@ SegmentProperties::SegmentProperties(QWidget *parent) :
m_ui_pane_c1.smooth->setVisible(false);
layout->addWidget(widget);
- connect(m_ui_pane_c1.p1_x, SIGNAL(valueChanged(double)), this, SLOT(c1Updated()));
- connect(m_ui_pane_c1.p1_y, SIGNAL(valueChanged(double)), this, SLOT(c1Updated()));
+ connect(m_ui_pane_c1.p1_x, &QDoubleSpinBox::valueChanged, this, &SegmentProperties::c1Updated);
+ connect(m_ui_pane_c1.p1_y, &QDoubleSpinBox::valueChanged, this, &SegmentProperties::c1Updated);
}
{
QWidget *widget = new QWidget(this);
@@ -53,8 +28,8 @@ SegmentProperties::SegmentProperties(QWidget *parent) :
m_ui_pane_c2.smooth->setVisible(false);
layout->addWidget(widget);
- connect(m_ui_pane_c2.p1_x, SIGNAL(valueChanged(double)), this, SLOT(c2Updated()));
- connect(m_ui_pane_c2.p1_y, SIGNAL(valueChanged(double)), this, SLOT(c2Updated()));
+ connect(m_ui_pane_c2.p1_x, &QDoubleSpinBox::valueChanged, this, &SegmentProperties::c2Updated);
+ connect(m_ui_pane_c2.p1_y, &QDoubleSpinBox::valueChanged, this, &SegmentProperties::c2Updated);
}
{
QWidget *widget = new QWidget(this);
@@ -62,9 +37,9 @@ SegmentProperties::SegmentProperties(QWidget *parent) :
m_ui_pane_p.label->setText("p1");
layout->addWidget(widget);
- connect(m_ui_pane_p.smooth, SIGNAL(toggled(bool)), this, SLOT(pUpdated()));
- connect(m_ui_pane_p.p1_x, SIGNAL(valueChanged(double)), this, SLOT(pUpdated()));
- connect(m_ui_pane_p.p1_y, SIGNAL(valueChanged(double)), this, SLOT(pUpdated()));
+ connect(m_ui_pane_p.smooth, &QCheckBox::toggled, this, &SegmentProperties::pUpdated);
+ connect(m_ui_pane_p.p1_x, &QDoubleSpinBox::valueChanged, this, &SegmentProperties::pUpdated);
+ connect(m_ui_pane_p.p1_y, &QDoubleSpinBox::valueChanged, this, &SegmentProperties::pUpdated);
}
}
diff --git a/tools/qmleasing/segmentproperties.h b/tools/qmleasing/segmentproperties.h
index b6bb6e2a5f..cfdea24e77 100644
--- a/tools/qmleasing/segmentproperties.h
+++ b/tools/qmleasing/segmentproperties.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef SEGMENTPROPERTIES_H
#define SEGMENTPROPERTIES_H
@@ -38,7 +13,7 @@ class SegmentProperties : public QWidget
{
Q_OBJECT
public:
- explicit SegmentProperties(QWidget *parent = 0);
+ explicit SegmentProperties(QWidget *parent = nullptr);
void setSplineEditor(SplineEditor *splineEditor)
{
m_splineEditor = splineEditor;
@@ -53,11 +28,7 @@ public:
invalidate();
}
-signals:
-
-public slots:
-
-private slots:
+private Q_SLOTS:
void c1Updated();
void c2Updated();
void pUpdated();
diff --git a/tools/qmleasing/splineeditor.cpp b/tools/qmleasing/splineeditor.cpp
index 99c161d475..dc71adafa4 100644
--- a/tools/qmleasing/splineeditor.cpp
+++ b/tools/qmleasing/splineeditor.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "splineeditor.h"
#include "segmentproperties.h"
@@ -194,7 +169,7 @@ void SplineEditor::paintEvent(QPaintEvent *)
paintControlPoint(QPointF(0.0, 0.0), &painter, false, true, false, false);
paintControlPoint(QPointF(1.0, 1.0), &painter, false, true, false, false);
- for (int i = 0; i < m_controlPoints.count() - 1; ++i)
+ for (int i = 0; i < m_controlPoints.size() - 1; ++i)
paintControlPoint(m_controlPoints.at(i),
&painter,
true,
@@ -290,7 +265,7 @@ QHash<QString, QEasingCurve> SplineEditor::presets() const
QString SplineEditor::generateCode()
{
QString s = QLatin1String("[");
- for (const QPointF &point : qAsConst(m_controlPoints)) {
+ for (const QPointF &point : std::as_const(m_controlPoints)) {
s += QString::number(point.x(), 'g', 2) + QLatin1Char(',')
+ QString::number(point.y(), 'g', 3) + QLatin1Char(',');
}
@@ -369,7 +344,7 @@ void SplineEditor::smoothPoint(int index)
before = m_controlPoints.at(index - 3);
QPointF after = QPointF(1.0, 1.0);
- if ((index + 3) < m_controlPoints.count())
+ if ((index + 3) < m_controlPoints.size())
after = m_controlPoints.at(index + 3);
QPointF tangent = (after - before) / 6;
@@ -379,7 +354,7 @@ void SplineEditor::smoothPoint(int index)
if (index > 0)
m_controlPoints[index - 1] = thisPoint - tangent;
- if (index + 1 < m_controlPoints.count())
+ if (index + 1 < m_controlPoints.size())
m_controlPoints[index + 1] = thisPoint + tangent;
m_smoothList[index / 3] = true;
@@ -397,7 +372,7 @@ void SplineEditor::cornerPoint(int index)
before = m_controlPoints.at(index - 3);
QPointF after = QPointF(1.0, 1.0);
- if ((index + 3) < m_controlPoints.count())
+ if ((index + 3) < m_controlPoints.size())
after = m_controlPoints.at(index + 3);
QPointF thisPoint = m_controlPoints.at(index);
@@ -405,7 +380,7 @@ void SplineEditor::cornerPoint(int index)
if (index > 0)
m_controlPoints[index - 1] = (before - thisPoint) / 3 + thisPoint;
- if (index + 1 < m_controlPoints.count())
+ if (index + 1 < m_controlPoints.size())
m_controlPoints[index + 1] = (after - thisPoint) / 3 + thisPoint;
m_smoothList[(index) / 3] = false;
@@ -437,7 +412,7 @@ void SplineEditor::addPoint(const QPointF point)
before = m_controlPoints.at(splitIndex);
QPointF after = QPointF(1.0, 1.0);
- if ((splitIndex + 3) < m_controlPoints.count())
+ if ((splitIndex + 3) < m_controlPoints.size())
after = m_controlPoints.at(splitIndex + 3);
if (splitIndex > 0) {
@@ -566,7 +541,7 @@ bool SplineEditor::isControlPointSmooth(int i) const
if (i == 0)
return false;
- if (i == m_controlPoints.count() - 1)
+ if (i == m_controlPoints.size() - 1)
return false;
if (m_numberOfSegments == 1)
@@ -577,7 +552,7 @@ bool SplineEditor::isControlPointSmooth(int i) const
if (index == 0)
return false;
- if (index == m_controlPoints.count() - 1)
+ if (index == m_controlPoints.size() - 1)
return false;
return m_smoothList.at(index / 3);
@@ -636,7 +611,7 @@ void SplineEditor::mouseMoveEvent(QMouseEvent *e)
if ((m_activeControlPoint > 1) && (m_activeControlPoint % 3) == 0) { //right control point
m_controlPoints[m_activeControlPoint - 2] -= distance;
- } else if ((m_activeControlPoint < (m_controlPoints.count() - 2)) //left control point
+ } else if ((m_activeControlPoint < (m_controlPoints.size() - 2)) //left control point
&& (m_activeControlPoint % 3) == 1) {
m_controlPoints[m_activeControlPoint + 2] -= distance;
}
@@ -653,7 +628,7 @@ void SplineEditor::setEasingCurve(const QEasingCurve &easingCurve)
m_block = true;
m_easingCurve = easingCurve;
m_controlPoints = m_easingCurve.toCubicSpline();
- m_numberOfSegments = m_controlPoints.count() / 3;
+ m_numberOfSegments = m_controlPoints.size() / 3;
update();
emit easingCurveChanged();
@@ -677,9 +652,9 @@ void SplineEditor::setEasingCurve(const QString &code)
if (code.startsWith(QLatin1Char('[')) && code.endsWith(QLatin1Char(']'))) {
const auto cleanCode = QStringView(code).mid(1, code.size() - 2);
const auto stringList = cleanCode.split(QLatin1Char(','), Qt::SkipEmptyParts);
- if (stringList.count() >= 6 && (stringList.count() % 6 == 0)) {
+ if (stringList.size() >= 6 && (stringList.size() % 6 == 0)) {
QVector<qreal> realList;
- realList.reserve(stringList.count());
+ realList.reserve(stringList.size());
for (const QStringView &string : stringList) {
bool ok;
realList.append(string.toDouble(&ok));
@@ -687,14 +662,14 @@ void SplineEditor::setEasingCurve(const QString &code)
return;
}
QVector<QPointF> points;
- const int count = realList.count() / 2;
+ const int count = realList.size() / 2;
points.reserve(count);
for (int i = 0; i < count; ++i)
points.append(QPointF(realList.at(i * 2), realList.at(i * 2 + 1)));
if (points.constLast() == QPointF(1.0, 1.0)) {
QEasingCurve easingCurve(QEasingCurve::BezierSpline);
- for (int i = 0; i < points.count() / 3; ++i) {
+ for (int i = 0; i < points.size() / 3; ++i) {
easingCurve.addCubicBezierSegment(points.at(i * 3),
points.at(i * 3 + 1),
points.at(i * 3 + 2));
diff --git a/tools/qmleasing/splineeditor.h b/tools/qmleasing/splineeditor.h
index 8dd47c3a89..3a1a7da3e9 100644
--- a/tools/qmleasing/splineeditor.h
+++ b/tools/qmleasing/splineeditor.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef SPLINEEDITOR_H
#define SPLINEEDITOR_H
@@ -46,7 +21,7 @@ class SplineEditor : public QWidget
Q_PROPERTY(QEasingCurve easingCurve READ easingCurve WRITE setEasingCurve NOTIFY easingCurveChanged);
public:
- explicit SplineEditor(QWidget *parent = 0);
+ explicit SplineEditor(QWidget *parent = nullptr);
QString generateCode();
QStringList presetNames() const;
QWidget *pointListWidget();
@@ -64,12 +39,12 @@ public:
//update();
}
-signals:
+Q_SIGNALS:
void easingCurveChanged();
void easingCurveCodeChanged(const QString &code);
-public slots:
+public Q_SLOTS:
void setEasingCurve(const QEasingCurve &easingCurve);
void setPreset(const QString &name);
void setEasingCurve(const QString &code);
diff --git a/tools/qmlformat/.prev_CMakeLists.txt b/tools/qmlformat/.prev_CMakeLists.txt
deleted file mode 100644
index 618e1bbfaa..0000000000
--- a/tools/qmlformat/.prev_CMakeLists.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-# Generated from qmlformat.pro.
-
-#####################################################################
-## qmlformat Tool:
-#####################################################################
-
-qt_get_tool_target_name(target_name qmlformat)
-qt_internal_add_tool(${target_name}
- TARGET_DESCRIPTION "QML Formatter"
- SOURCES
- commentastvisitor.cpp commentastvisitor.h
- dumpastvisitor.cpp dumpastvisitor.h
- main.cpp
- restructureastvisitor.cpp restructureastvisitor.h
- PUBLIC_LIBRARIES
- Qt::QmlDevToolsPrivate
-)
-
-#### Keys ignored in scope 1:.:.:qmlformat.pro:<TRUE>:
-# QMAKE_TARGET_DESCRIPTION = "QML" "Formatter"
-# _OPTION = "host_build"
diff --git a/tools/qmlformat/CMakeLists.txt b/tools/qmlformat/CMakeLists.txt
index e492a3ec56..908901b9f5 100644
--- a/tools/qmlformat/CMakeLists.txt
+++ b/tools/qmlformat/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qmlformat.pro.
#####################################################################
@@ -9,13 +12,13 @@ qt_internal_add_tool(${target_name}
TARGET_DESCRIPTION "QML Formatter"
TOOLS_TARGET Qml # special case
SOURCES
- commentastvisitor.cpp commentastvisitor.h
- dumpastvisitor.cpp dumpastvisitor.h
- main.cpp
- restructureastvisitor.cpp restructureastvisitor.h
- PUBLIC_LIBRARIES
- Qt::QmlDevToolsPrivate
+ qmlformat.cpp
+ LIBRARIES
+ Qt::Core
+ Qt::QmlDomPrivate
+ Qt::QmlToolingSettingsPrivate
)
+qt_internal_return_unless_building_tools()
#### Keys ignored in scope 1:.:.:qmlformat.pro:<TRUE>:
# QMAKE_TARGET_DESCRIPTION = "QML" "Formatter"
diff --git a/tools/qmlformat/commentastvisitor.cpp b/tools/qmlformat/commentastvisitor.cpp
deleted file mode 100644
index b8d916d3fb..0000000000
--- a/tools/qmlformat/commentastvisitor.cpp
+++ /dev/null
@@ -1,285 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "commentastvisitor.h"
-
-CommentAstVisitor::CommentAstVisitor(QQmlJS::Engine *engine, Node *rootNode) : m_engine(engine)
-{
- rootNode->accept(this);
-
- // Look for complete orphans that have not been attached to *any* node
- QVector<Comment> completeOrphans;
-
- for (const auto &comment : m_engine->comments()) {
- if (isCommentAttached(comment))
- continue;
-
- bool found_orphan = false;
- for (const auto &orphanList : orphanComments().values()) {
- for (const auto &orphan : orphanList) {
- if (orphan.contains(comment)) {
- found_orphan = true;
- break;
- }
- }
-
- if (found_orphan)
- break;
- }
-
- if (found_orphan)
- continue;
-
- completeOrphans.append(Comment(m_engine, Comment::Location::Front, {comment}));
- }
-
- m_orphanComments[nullptr] = completeOrphans;
-}
-
-QList<SourceLocation> CommentAstVisitor::findCommentsInLine(quint32 line, bool includePrevious) const
-{
- QList<SourceLocation> results;
- if (line == 0)
- return results;
-
- for (const auto &location : m_engine->comments()) {
- Comment comment(m_engine, Comment::Location::Front, { location });
- if (line < location.startLine || line > comment.endLine())
- continue;
-
- if (isCommentAttached(location))
- continue;
-
- results.append(location);
-
- if (includePrevious) {
- // See if we can find any more comments above this one
- auto previous = findCommentsInLine(location.startLine - 1, true);
-
- // Iterate it in reverse to restore the correct order
- for (auto it = previous.rbegin(); it != previous.rend(); it++) {
- results.prepend(*it);
- }
- }
-
- break;
- }
-
- return results;
-}
-
-bool CommentAstVisitor::isCommentAttached(const SourceLocation &location) const
-{
- for (const auto &value : m_attachedComments.values()) {
- if (value.contains(location))
- return true;
- }
-
- for (const auto &value : m_listItemComments.values()) {
- if (value.contains(location))
- return true;
- }
-
- // If a comment is already marked as an orphan of a Node that counts as attached too.
- for (const auto &orphanList : m_orphanComments.values()) {
- for (const auto &value : orphanList) {
- if (value.contains(location))
- return true;
- }
- }
-
- return false;
-}
-
-Comment CommentAstVisitor::findComment(SourceLocation first, SourceLocation last,
- int locations) const
-{
- if (locations & Comment::Location::Front) {
- quint32 searchAt = first.startLine - 1;
-
- const auto comments = findCommentsInLine(searchAt, true);
- if (!comments.isEmpty())
- return Comment(m_engine, Comment::Location::Front, comments);
- }
-
- if (locations & Comment::Location::Front_Inline) {
- quint32 searchAt = first.startLine;
-
- const auto comments = findCommentsInLine(searchAt);
- if (!comments.isEmpty())
- return Comment(m_engine, Comment::Location::Front_Inline, comments);
- }
-
- if (locations & Comment::Location::Back_Inline) {
- quint32 searchAt = last.startLine;
-
- const auto comments = findCommentsInLine(searchAt);
- if (!comments.isEmpty())
- return Comment(m_engine, Comment::Location::Back_Inline, comments);
- }
-
- if (locations & Comment::Location::Back) {
- quint32 searchAt = last.startLine + 1;
-
- const auto comments = findCommentsInLine(searchAt);
- if (!comments.isEmpty())
- return Comment(m_engine, Comment::Location::Back, comments);
- }
-
- return Comment();
-
-}
-
-Comment CommentAstVisitor::findComment(Node *node, int locations) const
-{
- return findComment(node->firstSourceLocation(), node->lastSourceLocation(), locations);
-}
-
-QVector<Comment> CommentAstVisitor::findOrphanComments(Node *node) const
-{
- QVector<Comment> comments;
-
- for (auto &comment : m_engine->comments()) {
- if (isCommentAttached(comment))
- continue;
-
- if (comment.begin() <= node->firstSourceLocation().begin()
- || comment.end() > node->lastSourceLocation().end()) {
- continue;
- }
-
- comments.append(Comment(m_engine, Comment::Location::Front, {comment}));
- }
-
- return comments;
-}
-
-void CommentAstVisitor::attachComment(Node *node, int locations)
-{
- auto comment = findComment(node, locations);
-
- if (comment.isValid())
- m_attachedComments[node] = comment;
-}
-
-bool CommentAstVisitor::visit(UiScriptBinding *node)
-{
- attachComment(node);
- return true;
-}
-
-bool CommentAstVisitor::visit(StatementList *node)
-{
- for (auto *item = node; item != nullptr; item = item->next)
- attachComment(item->statement, Comment::Front | Comment::Back_Inline);
- return true;
-}
-
-void CommentAstVisitor::endVisit(StatementList *node)
-{
- m_orphanComments[node] = findOrphanComments(node);
-}
-
-bool CommentAstVisitor::visit(UiObjectBinding *node)
-{
- attachComment(node, Comment::Front | Comment::Front_Inline | Comment::Back);
- return true;
-}
-
-bool CommentAstVisitor::visit(UiObjectDefinition *node)
-{
- attachComment(node, Comment::Front | Comment::Front_Inline | Comment::Back);
- return true;
-}
-
-void CommentAstVisitor::endVisit(UiObjectDefinition *node)
-{
- m_orphanComments[node] = findOrphanComments(node);
-}
-
-bool CommentAstVisitor::visit(UiArrayBinding *node)
-{
- attachComment(node);
- return true;
-}
-
-void CommentAstVisitor::endVisit(UiArrayBinding *node)
-{
- m_orphanComments[node] = findOrphanComments(node);
-}
-
-bool CommentAstVisitor::visit(UiEnumDeclaration *node)
-{
- attachComment(node);
- return true;
-}
-
-void CommentAstVisitor::endVisit(UiEnumDeclaration *node)
-{
- m_orphanComments[node] = findOrphanComments(node);
-}
-
-bool CommentAstVisitor::visit(UiEnumMemberList *node)
-{
- for (auto *item = node; item != nullptr; item = item->next) {
- auto comment = findComment(item->memberToken,
- item->valueToken.isValid() ? item->valueToken : item->memberToken,
- Comment::Front | Comment::Back_Inline);
-
- if (comment.isValid())
- m_listItemComments[item->memberToken.begin()] = comment;
- }
-
- m_orphanComments[node] = findOrphanComments(node);
-
- return true;
-}
-
-bool CommentAstVisitor::visit(UiPublicMember *node)
-{
- attachComment(node);
- return true;
-}
-
-bool CommentAstVisitor::visit(FunctionDeclaration *node)
-{
- attachComment(node);
- return true;
-}
-
-bool CommentAstVisitor::visit(UiImport *node)
-{
- attachComment(node);
- return true;
-}
-
-bool CommentAstVisitor::visit(UiPragma *node)
-{
- attachComment(node);
- return true;
-}
diff --git a/tools/qmlformat/commentastvisitor.h b/tools/qmlformat/commentastvisitor.h
deleted file mode 100644
index 09bc786985..0000000000
--- a/tools/qmlformat/commentastvisitor.h
+++ /dev/null
@@ -1,143 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef COMMENTASTVISITOR_H
-#define COMMENTASTVISITOR_H
-
-#include <QtQml/private/qqmljsastvisitor_p.h>
-#include <QtQml/private/qqmljsast_p.h>
-#include <QtQml/private/qqmljsengine_p.h>
-
-#include <QHash>
-#include <QString>
-#include <QVector>
-
-using namespace QQmlJS::AST;
-using namespace QQmlJS;
-
-struct Comment
-{
- enum Location : int
- {
- Front = 1,
- Front_Inline = Front << 1,
- Back = Front_Inline << 1,
- Back_Inline = Back << 1,
- DefaultLocations = Front | Back_Inline,
- AllLocations = Front | Back | Front_Inline | Back_Inline
- } m_location = Front;
-
- Comment() = default;
- Comment(const QQmlJS::Engine *engine, Location location, QList<SourceLocation> srcLocations)
- : m_location(location), m_srcLocations(srcLocations) {
- for (const auto& srcLoc : srcLocations) {
- m_text += engine->code().mid(static_cast<int>(srcLoc.begin()),
- static_cast<int>(srcLoc.end() - srcLoc.begin())) + "\n";
- }
-
- m_text.chop(1);
- }
-
- QList<SourceLocation> m_srcLocations;
-
- bool hasSheBang() const { return !m_srcLocations.isEmpty() && m_srcLocations.first().begin() == 0; }
- bool isValid() const { return !m_srcLocations.isEmpty(); }
- bool isMultiline() const { return m_text.contains("\n"); }
- bool isSyntheticMultiline() const { return m_srcLocations.size() > 1; }
-
- bool contains(const SourceLocation& location) const {
- for (const SourceLocation& srcLoc : m_srcLocations) {
- if (srcLoc.begin() == location.begin() && srcLoc.end() == location.end())
- return true;
- }
-
- return false;
- }
-
- quint32 endLine() const
- {
- if (isSyntheticMultiline() || !isValid())
- return 0;
-
- return m_srcLocations[0].startLine + m_text.count(QLatin1Char('\n'));
- }
-
- QString m_text;
-};
-
-class CommentAstVisitor : protected Visitor
-{
-public:
- CommentAstVisitor(QQmlJS::Engine *engine, Node *rootNode);
-
- void throwRecursionDepthError() override {}
-
- const QHash<Node *, Comment> attachedComments() const { return m_attachedComments; }
- const QHash<quint32, Comment> listComments() const { return m_listItemComments; }
- const QHash<Node *, QVector<Comment>> orphanComments() const { return m_orphanComments; }
-
- bool visit(UiScriptBinding *node) override;
- bool visit(UiObjectBinding *node) override;
-
- bool visit(UiArrayBinding *node) override;
- void endVisit(UiArrayBinding *node) override;
-
- bool visit(UiObjectDefinition *node) override;
- void endVisit(UiObjectDefinition *) override;
-
- bool visit(UiEnumDeclaration *node) override;
- void endVisit(UiEnumDeclaration *node) override;
-
- bool visit(UiEnumMemberList *node) override;
-
- bool visit(StatementList *node) override;
- void endVisit(StatementList *node) override;
-
- bool visit(UiImport *node) override;
- bool visit(UiPragma *node) override;
- bool visit(UiPublicMember *node) override;
- bool visit(FunctionDeclaration *node) override;
-private:
- bool isCommentAttached(const SourceLocation& location) const;
-
- QList<SourceLocation> findCommentsInLine(quint32 line, bool includePrevious = false) const;
-
- Comment findComment(SourceLocation first, SourceLocation last,
- int locations = Comment::DefaultLocations) const;
-
- Comment findComment(Node *node, int locations = Comment::DefaultLocations) const;
- QVector<Comment> findOrphanComments(Node *node) const;
- void attachComment(Node *node, int locations = Comment::DefaultLocations);
-
- QQmlJS::Engine *m_engine;
- QHash<Node *, Comment> m_attachedComments;
- QHash<quint32, Comment> m_listItemComments;
- QHash<Node *, QVector<Comment>> m_orphanComments;
-};
-
-#endif // COMMENTASTVISITOR_H
diff --git a/tools/qmlformat/dumpastvisitor.cpp b/tools/qmlformat/dumpastvisitor.cpp
deleted file mode 100644
index 723be4e445..0000000000
--- a/tools/qmlformat/dumpastvisitor.cpp
+++ /dev/null
@@ -1,1433 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "dumpastvisitor.h"
-
-#include <QtQml/private/qqmljslexer_p.h>
-
-DumpAstVisitor::DumpAstVisitor(QQmlJS::Engine *engine, Node *rootNode, CommentAstVisitor *comment,
- int indentWidth, DumpAstVisitor::Indentation indentation)
- : m_engine(engine), m_comment(comment), m_indentWidth(indentWidth), m_indentation(indentation)
-{
- // Add all completely orphaned comments
- m_result += getOrphanedComments(nullptr);
-
- m_scope_properties.push(ScopeProperties {});
-
- rootNode->accept(this);
-
- // We need to get rid of one new-line so our output doesn't append an empty line
- m_result.chop(1);
-
- // Remove trailing whitespace
- QStringList lines = m_result.split("\n");
- for (QString& line : lines) {
- while (line.endsWith(" "))
- line.chop(1);
- }
-
- m_result = lines.join("\n");
-}
-
-bool DumpAstVisitor::preVisit(Node *el)
-{
- UiObjectMember *m = el->uiObjectMemberCast();
- if (m != 0)
- Node::accept(m->annotations, this);
- return true;
-}
-
-static QString parseUiQualifiedId(UiQualifiedId *id)
-{
- QString name = id->name.toString();
- for (auto *item = id->next; item != nullptr; item = item->next) {
- name += "." + item->name;
- }
-
- return name;
-}
-
-static QString operatorToString(int op)
-{
- switch (op)
- {
- case QSOperator::Add: return "+";
- case QSOperator::And: return "&&";
- case QSOperator::InplaceAnd: return "&=";
- case QSOperator::Assign: return "=";
- case QSOperator::BitAnd: return "&";
- case QSOperator::BitOr: return "|";
- case QSOperator::BitXor: return "^";
- case QSOperator::InplaceSub: return "-=";
- case QSOperator::Div: return "/";
- case QSOperator::InplaceDiv: return "/=";
- case QSOperator::Equal: return "==";
- case QSOperator::Exp: return "**";
- case QSOperator::InplaceExp: return "**=";
- case QSOperator::Ge: return ">=";
- case QSOperator::Gt: return ">";
- case QSOperator::In: return "in";
- case QSOperator::InplaceAdd: return "+=";
- case QSOperator::InstanceOf: return "instanceof";
- case QSOperator::Le: return "<=";
- case QSOperator::LShift: return "<<";
- case QSOperator::InplaceLeftShift: return "<<=";
- case QSOperator::Lt: return "<";
- case QSOperator::Mod: return "%";
- case QSOperator::InplaceMod: return "%=";
- case QSOperator::Mul: return "*";
- case QSOperator::InplaceMul: return "*=";
- case QSOperator::NotEqual: return "!=";
- case QSOperator::Or: return "||";
- case QSOperator::InplaceOr: return "|=";
- case QSOperator::RShift: return ">>";
- case QSOperator::InplaceRightShift: return ">>=";
- case QSOperator::StrictEqual: return "===";
- case QSOperator::StrictNotEqual: return "!==";
- case QSOperator::Sub: return "-";
- case QSOperator::URShift: return ">>>";
- case QSOperator::InplaceURightShift: return ">>>=";
- case QSOperator::InplaceXor: return "^=";
- case QSOperator::As: return "as";
- case QSOperator::Coalesce: return "??";
- case QSOperator::Invalid:
- default:
- return "INVALID";
- }
-}
-
-QString DumpAstVisitor::formatComment(const Comment &comment) const
-{
- QString result;
-
- bool useMultilineComment = comment.isMultiline() && !comment.isSyntheticMultiline();
-
- if (useMultilineComment)
- result += "/*";
- else if (!comment.hasSheBang())
- result += "//";
-
- result += comment.m_text;
-
- if (comment.isSyntheticMultiline())
- result = result.replace("\n","\n" + formatLine("//", false));
-
- if (comment.m_location == Comment::Location::Back_Inline)
- result.prepend(" ");
-
- if (useMultilineComment)
- result += "*/";
-
- return result;
-}
-
-QString DumpAstVisitor::getComment(Node *node, Comment::Location location) const
-{
- const auto& comments = m_comment->attachedComments();
- if (!comments.contains(node))
- return "";
-
- auto comment = comments[node];
-
- if (comment.m_location != location)
- return "";
-
- return formatComment(comment);
-}
-
-QString DumpAstVisitor::getListItemComment(SourceLocation srcLocation,
- Comment::Location location) const {
- const auto& comments = m_comment->listComments();
-
- if (!comments.contains(srcLocation.begin()))
- return "";
-
- auto comment = comments[srcLocation.begin()];
-
- if (comment.m_location != location)
- return "";
-
- return formatComment(comment);
-}
-
-QString DumpAstVisitor::getOrphanedComments(Node *node) const {
- const auto& orphans = m_comment->orphanComments()[node];
-
- if (orphans.size() == 0)
- return "";
-
- QString result = "";
-
- for (const Comment& orphan : orphans) {
- result += formatLine(formatComment(orphan));
- }
-
- result += "\n";
-
- return result;
-}
-
-QString DumpAstVisitor::parseArgumentList(ArgumentList *list)
-{
- QString result = "";
-
- for (auto *item = list; item != nullptr; item = item->next)
- result += parseExpression(item->expression) + (item->next != nullptr ? ", " : "");
-
- return result;
-}
-
-QString DumpAstVisitor::parseUiParameterList(UiParameterList *list) {
- QString result = "";
-
- for (auto *item = list; item != nullptr; item = item->next)
- result += parseUiQualifiedId(item->type) + " " + item->name + (item->next != nullptr ? ", " : "");
-
- return result;
-}
-
-QString DumpAstVisitor::parsePatternElement(PatternElement *element, bool scope)
-{
- switch (element->type)
- {
- case PatternElement::Literal:
- return parseExpression(element->initializer);
- case PatternElement::Binding: {
- QString result = "";
- QString expr = parseExpression(element->initializer);
-
- if (scope) {
- switch (element->scope) {
- case VariableScope::NoScope:
- break;
- case VariableScope::Let:
- result = "let ";
- break;
- case VariableScope::Const:
- result = "const ";
- break;
- case VariableScope::Var:
- result = "var ";
- break;
- }
- }
-
- if (element->bindingIdentifier.isEmpty())
- result += parseExpression(element->bindingTarget);
- else
- result += element->bindingIdentifier.toString();
-
- if (element->typeAnnotation != nullptr)
- result += ": " + parseType(element->typeAnnotation->type);
-
- if (!expr.isEmpty())
- result += " = "+expr;
-
- return result;
- }
- default:
- m_error = true;
- return "pe_unknown";
- }
-}
-
-static QString escapeString(QString string)
-{
- // Handle escape sequences
- string = string.replace("\r", "\\r").replace("\n", "\\n").replace("\t", "\\t")
- .replace("\b","\\b").replace("\v", "\\v").replace("\f", "\\f");
-
- // Escape backslash
- string = string.replace("\\", "\\\\");
-
- // Escape "
- string = string.replace("\"", "\\\"");
-
- return "\"" + string + "\"";
-}
-
-QString DumpAstVisitor::parsePatternElementList(PatternElementList *list)
-{
- QString result = "";
-
- for (auto *item = list; item != nullptr; item = item->next)
- result += parsePatternElement(item->element) + (item->next != nullptr ? ", " : "");
-
- return result;
-}
-
-QString DumpAstVisitor::parseFormalParameterList(FormalParameterList *list)
-{
- QString result = "";
-
- for (auto *item = list; item != nullptr; item = item->next)
- result += parsePatternElement(item->element) + (item->next != nullptr ? ", " : "");
-
- return result;
-}
-
-QString DumpAstVisitor::parsePatternProperty(PatternProperty *property)
-{
- switch (property->type) {
- case PatternElement::Getter:
- return "get "+parseFunctionExpression(cast<FunctionExpression *>(property->initializer), true);
- case PatternElement::Setter:
- return "set "+parseFunctionExpression(cast<FunctionExpression *>(property->initializer), true);
- default:
- if (property->name->kind == Node::Kind_ComputedPropertyName) {
- return "["+parseExpression(cast<ComputedPropertyName *>(property->name)->expression)+"]: "+parsePatternElement(property, false);
- } else {
- return escapeString(property->name->asString())+": "+parsePatternElement(property, false);
- }
- }
-}
-
-QString DumpAstVisitor::parsePatternPropertyList(PatternPropertyList *list)
-{
- QString result = "";
-
- for (auto *item = list; item != nullptr; item = item->next) {
- result += formatLine(parsePatternProperty(item->property) + (item->next != nullptr ? "," : ""));
- }
-
- return result;
-}
-
-QString DumpAstVisitor::parseFunctionExpression(FunctionExpression *functExpr, bool omitFunction)
-{
- m_indentLevel++;
- QString result;
- bool hasBraces = true;
-
- if (!functExpr->isArrowFunction) {
- result += omitFunction ? "" : "function";
-
- if (functExpr->isGenerator)
- result += "*";
-
- if (!functExpr->name.isEmpty())
- result += (omitFunction ? "" : " ") + functExpr->name;
-
- result += "("+parseFormalParameterList(functExpr->formals)+")";
-
- if (functExpr->typeAnnotation != nullptr)
- result += " : " + parseType(functExpr->typeAnnotation->type);
-
- result += " {\n" + parseStatementList(functExpr->body);
- } else {
- result += "("+parseFormalParameterList(functExpr->formals)+")";
-
- if (functExpr->typeAnnotation != nullptr)
- result += " : " + parseType(functExpr->typeAnnotation->type);
-
- result += " => ";
-
- if (functExpr->body == nullptr) {
- result += "{}";
- } else if (functExpr->body->next == nullptr && functExpr->body->statement->kind == Node::Kind_ReturnStatement) {
- m_indentLevel--;
- result += parseExpression(cast<ReturnStatement *>(functExpr->body->statement)->expression);
- hasBraces = false;
- } else {
- result += "{\n" + parseStatementList(functExpr->body);
- }
- }
-
- if (hasBraces) {
- m_indentLevel--;
- result += formatLine("}", false);
- }
-
- return result;
-
-}
-
-QString DumpAstVisitor::parseType(Type *type) {
- QString result = parseUiQualifiedId(type->typeId);
-
- if (type->typeArguments != nullptr) {
- TypeArgumentList *list = cast<TypeArgumentList *>(type->typeArguments);
-
- result += "<";
-
- for (auto *item = list; item != nullptr; item = item->next) {
- result += parseType(item->typeId) + (item->next != nullptr ? ", " : "");
- }
-
- result += ">";
- }
-
- return result;
-}
-
-QString DumpAstVisitor::parseExpression(ExpressionNode *expression)
-{
- if (expression == nullptr)
- return "";
-
- switch (expression->kind)
- {
- case Node::Kind_ArrayPattern:
- return "["+parsePatternElementList(cast<ArrayPattern *>(expression)->elements)+"]";
- case Node::Kind_IdentifierExpression:
- return cast<IdentifierExpression*>(expression)->name.toString();
- case Node::Kind_FieldMemberExpression: {
- auto *fieldMemberExpr = cast<FieldMemberExpression *>(expression);
- QString result = parseExpression(fieldMemberExpr->base);
-
- // If we're operating on a numeric literal, always put it in braces
- if (fieldMemberExpr->base->kind == Node::Kind_NumericLiteral)
- result = "(" + result + ")";
-
- result += "." + fieldMemberExpr->name.toString();
-
- return result;
- }
- case Node::Kind_ArrayMemberExpression: {
- auto *arrayMemberExpr = cast<ArrayMemberExpression *>(expression);
- return parseExpression(arrayMemberExpr->base)
- + "[" + parseExpression(arrayMemberExpr->expression) + "]";
- }
- case Node::Kind_NestedExpression:
- return "("+parseExpression(cast<NestedExpression *>(expression)->expression)+")";
- case Node::Kind_TrueLiteral:
- return "true";
- case Node::Kind_FalseLiteral:
- return "false";
- case Node::Kind_FunctionExpression:
- {
- auto *functExpr = cast<FunctionExpression *>(expression);
- return parseFunctionExpression(functExpr);
- }
- case Node::Kind_NullExpression:
- return "null";
- case Node::Kind_ThisExpression:
- return "this";
- case Node::Kind_PostIncrementExpression:
- return parseExpression(cast<PostIncrementExpression *>(expression)->base)+"++";
- case Node::Kind_PreIncrementExpression:
- return "++"+parseExpression(cast<PreIncrementExpression *>(expression)->expression);
- case Node::Kind_PostDecrementExpression:
- return parseExpression(cast<PostDecrementExpression *>(expression)->base)+"--";
- case Node::Kind_PreDecrementExpression:
- return "--"+parseExpression(cast<PreDecrementExpression *>(expression)->expression);
- case Node::Kind_NumericLiteral:
- return QString::number(cast<NumericLiteral *>(expression)->value);
- case Node::Kind_TemplateLiteral: {
- auto firstSrcLoc = cast<TemplateLiteral *>(expression)->firstSourceLocation();
- auto lastSrcLoc = cast<TemplateLiteral *>(expression)->lastSourceLocation();
- return m_engine->code().mid(static_cast<int>(firstSrcLoc.begin()),
- static_cast<int>(lastSrcLoc.end() - firstSrcLoc.begin()));
- }
- case Node::Kind_StringLiteral: {
- auto srcLoc = cast<StringLiteral *>(expression)->firstSourceLocation();
- return m_engine->code().mid(static_cast<int>(srcLoc.begin()),
- static_cast<int>(srcLoc.end() - srcLoc.begin()));
- }
- case Node::Kind_BinaryExpression: {
- auto *binExpr = expression->binaryExpressionCast();
- return parseExpression(binExpr->left) + " " + operatorToString(binExpr->op)
- + " " + parseExpression(binExpr->right);
- }
- case Node::Kind_CallExpression: {
- auto *callExpr = cast<CallExpression *>(expression);
-
- return parseExpression(callExpr->base) + "(" + parseArgumentList(callExpr->arguments) + ")";
- }
- case Node::Kind_NewExpression:
- return "new "+parseExpression(cast<NewExpression *>(expression)->expression);
- case Node::Kind_NewMemberExpression: {
- auto *newMemberExpression = cast<NewMemberExpression *>(expression);
- return "new "+parseExpression(newMemberExpression->base)
- + "(" +parseArgumentList(newMemberExpression->arguments)+")";
- }
- case Node::Kind_DeleteExpression:
- return "delete " + parseExpression(cast<DeleteExpression *>(expression)->expression);
- case Node::Kind_VoidExpression:
- return "void " + parseExpression(cast<VoidExpression *>(expression)->expression);
- case Node::Kind_TypeOfExpression:
- return "typeof " + parseExpression(cast<TypeOfExpression *>(expression)->expression);
- case Node::Kind_UnaryPlusExpression:
- return "+" + parseExpression(cast<UnaryPlusExpression *>(expression)->expression);
- case Node::Kind_UnaryMinusExpression:
- return "-" + parseExpression(cast<UnaryMinusExpression *>(expression)->expression);
- case Node::Kind_NotExpression:
- return "!" + parseExpression(cast<NotExpression *>(expression)->expression);
- case Node::Kind_TildeExpression:
- return "~" + parseExpression(cast<TildeExpression *>(expression)->expression);
- case Node::Kind_ConditionalExpression: {
- auto *condExpr = cast<ConditionalExpression *>(expression);
-
- QString result = "";
-
- result += parseExpression(condExpr->expression) + " ? ";
- result += parseExpression(condExpr->ok) + " : ";
- result += parseExpression(condExpr->ko);
-
- return result;
- }
- case Node::Kind_YieldExpression: {
- auto *yieldExpr = cast<YieldExpression*>(expression);
-
- QString result = "yield";
-
- if (yieldExpr->isYieldStar)
- result += "*";
-
- if (yieldExpr->expression)
- result += " " + parseExpression(yieldExpr->expression);
-
- return result;
- }
- case Node::Kind_ObjectPattern: {
- auto *objectPattern = cast<ObjectPattern*>(expression);
-
- if (objectPattern->properties == nullptr)
- return "{}";
-
- QString result = "{\n";
-
- m_indentLevel++;
- result += parsePatternPropertyList(objectPattern->properties);
- m_indentLevel--;
-
- result += formatLine("}", false);
-
- return result;
- }
- case Node::Kind_Expression: {
- auto* expr = cast<Expression*>(expression);
- return parseExpression(expr->left)+", "+parseExpression(expr->right);
- }
- case Node::Kind_TypeExpression: {
- auto* type = cast<TypeExpression*>(expression);
- return parseType(type->m_type);
- }
- case Node::Kind_RegExpLiteral: {
- auto* regexpLiteral = cast<RegExpLiteral*>(expression);
- QString result = "/"+regexpLiteral->pattern+"/";
-
- if (regexpLiteral->flags & QQmlJS::Lexer::RegExp_Unicode)
- result += "u";
- if (regexpLiteral->flags & QQmlJS::Lexer::RegExp_Global)
- result += "g";
- if (regexpLiteral->flags & QQmlJS::Lexer::RegExp_Multiline)
- result += "m";
- if (regexpLiteral->flags & QQmlJS::Lexer::RegExp_Sticky)
- result += "y";
- if (regexpLiteral->flags & QQmlJS::Lexer::RegExp_IgnoreCase)
- result += "i";
-
- return result;
- }
- default:
- m_error = true;
- return "unknown_expression_"+QString::number(expression->kind);
- }
-}
-
-QString DumpAstVisitor::parseVariableDeclarationList(VariableDeclarationList *list)
-{
- QString result = "";
-
- for (auto *item = list; item != nullptr; item = item->next) {
- result += parsePatternElement(item->declaration, (item == list))
- + (item->next != nullptr ? ", " : "");
- }
-
- return result;
-}
-
-QString DumpAstVisitor::parseCaseBlock(CaseBlock *block)
-{
- QString result = "{\n";
-
- for (auto *item = block->clauses; item != nullptr; item = item->next) {
- result += formatLine("case "+parseExpression(item->clause->expression)+":");
- m_indentLevel++;
- result += parseStatementList(item->clause->statements);
- m_indentLevel--;
- }
-
- if (block->defaultClause) {
- result += formatLine("default:");
- m_indentLevel++;
- result += parseStatementList(block->defaultClause->statements);
- m_indentLevel--;
- }
-
- result += formatLine("}", false);
-
- return result;
-}
-
-QString DumpAstVisitor::parseExportSpecifier(ExportSpecifier *specifier)
-{
- QString result = specifier->identifier.toString();
-
- if (!specifier->exportedIdentifier.isEmpty())
- result += " as " + specifier->exportedIdentifier;
-
- return result;
-}
-
-QString DumpAstVisitor::parseExportsList(ExportsList *list)
-{
- QString result = "";
-
- for (auto *item = list; item != nullptr; item = item->next) {
- result += formatLine(parseExportSpecifier(item->exportSpecifier)
- + (item->next != nullptr ? "," : ""));
- }
-
- return result;
-}
-
-static bool needsSemicolon(int kind)
-{
- switch (kind) {
- case Node::Kind_ForStatement:
- case Node::Kind_ForEachStatement:
- case Node::Kind_IfStatement:
- case Node::Kind_SwitchStatement:
- case Node::Kind_WhileStatement:
- case Node::Kind_DoWhileStatement:
- case Node::Kind_TryStatement:
- case Node::Kind_WithStatement:
- return false;
- default:
- return true;
- }
-}
-
-QString DumpAstVisitor::parseBlock(Block *block, bool hasNext, bool allowBraceless)
-{
- bool hasOneLine =
- (block->statements != nullptr && block->statements->next == nullptr) && allowBraceless;
-
- QString result = hasOneLine ? "\n" : "{\n";
- m_indentLevel++;
- result += parseStatementList(block->statements);
- m_indentLevel--;
-
- if (hasNext)
- result += formatLine(hasOneLine ? "" : "} ", false);
-
- if (!hasNext && !hasOneLine)
- result += formatLine("}", false);
-
- if (block->statements) {
- m_blockNeededBraces |= !needsSemicolon(block->statements->statement->kind)
- || (block->statements->next != nullptr);
- } else {
- m_blockNeededBraces = true;
- }
-
- return result;
-}
-
-static bool endsWithSemicolon(const QStringView s)
-{
- return s.trimmed().endsWith(';');
-}
-
-QString DumpAstVisitor::parseStatement(Statement *statement, bool blockHasNext,
- bool blockAllowBraceless)
-{
- if (statement == nullptr)
- return "";
-
- switch (statement->kind)
- {
- case Node::Kind_EmptyStatement:
- return "";
- case Node::Kind_ExpressionStatement:
- return parseExpression(cast<ExpressionStatement *>(statement)->expression);
- case Node::Kind_VariableStatement:
- return parseVariableDeclarationList(cast<VariableStatement *>(statement)->declarations);
- case Node::Kind_ReturnStatement:
- return "return "+parseExpression(cast<ReturnStatement *>(statement)->expression);
- case Node::Kind_ContinueStatement:
- return "continue";
- case Node::Kind_BreakStatement:
- return "break";
- case Node::Kind_SwitchStatement: {
- auto *switchStatement = cast<SwitchStatement *>(statement);
-
- QString result = "switch ("+parseExpression(switchStatement->expression)+") ";
-
- result += parseCaseBlock(switchStatement->block);
-
- return result;
- }
- case Node::Kind_IfStatement: {
- auto *ifStatement = cast<IfStatement *>(statement);
-
- m_blockNeededBraces = !blockAllowBraceless;
-
- QString ifFalse = parseStatement(ifStatement->ko, false, true);
- QString ifTrue = parseStatement(ifStatement->ok, !ifFalse.isEmpty(), true);
-
- bool ifTrueBlock = ifStatement->ok->kind == Node::Kind_Block;
- bool ifFalseBlock = ifStatement->ko
- ? (ifStatement->ko->kind == Node::Kind_Block || ifStatement->ko->kind == Node::Kind_IfStatement)
- : false;
-
- if (m_blockNeededBraces) {
- ifFalse = parseStatement(ifStatement->ko, false, false);
- ifTrue = parseStatement(ifStatement->ok, !ifFalse.isEmpty(), false);
- }
-
- if (ifStatement->ok->kind != Node::Kind_Block && !endsWithSemicolon(ifTrue))
- ifTrue += ";";
-
- if (ifStatement->ko && ifStatement->ko->kind != Node::Kind_Block
- && ifStatement->ko->kind != Node::Kind_IfStatement && !endsWithSemicolon(ifFalse))
- ifFalse += ";";
-
- QString result = "if (" + parseExpression(ifStatement->expression) + ")";
-
- if (m_blockNeededBraces) {
- if (ifStatement->ok->kind != Node::Kind_Block) {
- QString result = "{\n";
- m_indentLevel++;
- result += formatLine(ifTrue);
- m_indentLevel--;
- result += formatLine("} ", false);
- ifTrue = result;
- ifTrueBlock = true;
- }
-
- if (ifStatement->ko && ifStatement->ko->kind != Node::Kind_Block && ifStatement->ko->kind != Node::Kind_IfStatement) {
- QString result = "{\n";
- m_indentLevel++;
- result += formatLine(ifFalse);
- m_indentLevel--;
- result += formatLine("} ", false);
- ifFalse = result;
- ifFalseBlock = true;
- }
- }
-
- if (ifTrueBlock) {
- result += " " + ifTrue;
- } else {
- result += "\n";
- m_indentLevel++;
- result += formatPartlyFormatedLines(ifTrue);
- m_indentLevel--;
- }
-
- if (!ifFalse.isEmpty())
- {
- if (ifTrueBlock)
- result += "else";
- else
- result += formatLine("else", false);
-
- if (ifFalseBlock) {
- // Blocks generate an extra newline that we don't want here.
- if (!m_blockNeededBraces && ifFalse.endsWith(QLatin1String("\n")))
- ifFalse.chop(1);
-
- result += " " + ifFalse;
- } else {
- result += "\n";
- m_indentLevel++;
- result += formatPartlyFormatedLines(ifFalse, false);
- m_indentLevel--;
- }
- }
-
- return result;
- }
- case Node::Kind_ForStatement: {
- auto *forStatement = cast<ForStatement *>(statement);
-
- QString expr = parseExpression(forStatement->expression);
- QString result = "for (";
-
- result += parseVariableDeclarationList(forStatement->declarations);
-
- result += "; ";
-
- result += parseExpression(forStatement->condition) + "; ";
- result += parseExpression(forStatement->expression)+")";
-
- const QString statement = parseStatement(forStatement->statement);
-
- if (!statement.isEmpty())
- result += " "+statement;
- else
- result += ";";
-
- return result;
- }
- case Node::Kind_ForEachStatement: {
- auto *forEachStatement = cast<ForEachStatement *>(statement);
-
- QString result = "for (";
-
- PatternElement *patternElement = cast<PatternElement *>(forEachStatement->lhs);
-
- if (patternElement != nullptr)
- result += parsePatternElement(patternElement);
- else
- result += parseExpression(forEachStatement->lhs->expressionCast());
-
- switch (forEachStatement->type)
- {
- case ForEachType::In:
- result += " in ";
- break;
- case ForEachType::Of:
- result += " of ";
- break;
- }
-
- result += parseExpression(forEachStatement->expression) + ")";
-
- const QString statement = parseStatement(forEachStatement->statement);
-
- if (!statement.isEmpty())
- result += " "+statement;
- else
- result += ";";
-
- return result;
- }
- case Node::Kind_WhileStatement: {
- auto *whileStatement = cast<WhileStatement *>(statement);
-
- m_blockNeededBraces = false;
-
- auto statement = parseStatement(whileStatement->statement, false, true);
-
- QString result = "while ("+parseExpression(whileStatement->expression) + ")";
-
- if (!statement.isEmpty())
- result += (m_blockNeededBraces ? " " : "") + statement;
- else
- result += ";";
-
- return result;
- }
- case Node::Kind_DoWhileStatement: {
- auto *doWhileStatement = cast<DoWhileStatement *>(statement);
- return "do " + parseBlock(cast<Block *>(doWhileStatement->statement), true, false)
- + "while (" + parseExpression(doWhileStatement->expression) + ")";
- }
- case Node::Kind_TryStatement: {
- auto *tryStatement = cast<TryStatement *>(statement);
-
- Catch *catchExpr = tryStatement->catchExpression;
- Finally *finallyExpr = tryStatement->finallyExpression;
-
- QString result;
-
- result += "try " + parseBlock(cast<Block *>(tryStatement->statement), true, false);
-
- result += "catch (" + parsePatternElement(catchExpr->patternElement, false) + ") "
- + parseBlock(cast<Block *>(catchExpr->statement), finallyExpr, false);
-
- if (finallyExpr) {
- result += "finally " + parseBlock(cast<Block *>(tryStatement->statement), false, false);
- }
-
- return result;
- }
- case Node::Kind_Block: {
- return parseBlock(cast<Block *>(statement), blockHasNext, blockAllowBraceless);
- }
- case Node::Kind_ThrowStatement:
- return "throw "+parseExpression(cast<ThrowStatement *>(statement)->expression);
- case Node::Kind_LabelledStatement: {
- auto *labelledStatement = cast<LabelledStatement *>(statement);
- QString result = labelledStatement->label+":\n";
- result += formatLine(parseStatement(labelledStatement->statement), false);
-
- return result;
- }
- case Node::Kind_WithStatement: {
- auto *withStatement = cast<WithStatement *>(statement);
- return "with (" + parseExpression(withStatement->expression) + ") "
- + parseStatement(withStatement->statement);
- }
- case Node::Kind_DebuggerStatement: {
- return "debugger";
- }
- case Node::Kind_ExportDeclaration:
- m_error = true;
- return "export_decl_unsupported";
- case Node::Kind_ImportDeclaration:
- m_error = true;
- return "import_decl_unsupported";
- default:
- m_error = true;
- return "unknown_statement_"+QString::number(statement->kind);
- }
-}
-
-QString DumpAstVisitor::parseStatementList(StatementList *list)
-{
- QString result = "";
-
- if (list == nullptr)
- return "";
-
- result += getOrphanedComments(list);
-
- for (auto *item = list; item != nullptr; item = item->next) {
- QString statement = parseStatement(item->statement->statementCast(), false, true);
- if (statement.isEmpty())
- continue;
-
- QString commentFront = getComment(item->statement, Comment::Location::Front);
- QString commentBackInline = getComment(item->statement, Comment::Location::Back_Inline);
-
- if (!commentFront.isEmpty())
- result += formatLine(commentFront);
-
- result += formatLine(statement + (needsSemicolon(item->statement->kind) ? ";" : "")
- + commentBackInline);
- }
-
- return result;
-}
-
-bool DumpAstVisitor::visit(UiPublicMember *node) {
-
- QString commentFront = getComment(node, Comment::Location::Front);
- QString commentBackInline = getComment(node, Comment::Location::Back_Inline);
-
- switch (node->type)
- {
- case UiPublicMember::Signal:
- if (scope().m_firstSignal) {
- if (scope().m_firstOfAll)
- scope().m_firstOfAll = false;
- else
- addNewLine();
-
- scope().m_firstSignal = false;
- }
-
- addLine(commentFront);
- addLine("signal "+node->name.toString()+"("+parseUiParameterList(node->parameters) + ")"
- + commentBackInline);
- break;
- case UiPublicMember::Property: {
- if (scope().m_firstProperty) {
- if (scope().m_firstOfAll)
- scope().m_firstOfAll = false;
- else
- addNewLine();
-
- scope().m_firstProperty = false;
- }
-
- const bool is_required = node->requiredToken.isValid();
- const bool is_default = node->defaultToken.isValid();
- const bool is_readonly = node->readonlyToken.isValid();
- const bool has_type_modifier = node->typeModifierToken.isValid();
-
- QString prefix = "";
- QString statement = parseStatement(node->statement);
-
- if (!statement.isEmpty())
- statement.prepend(": ");
-
- if (is_required)
- prefix += "required ";
-
- if (is_default)
- prefix += "default ";
-
- if (is_readonly)
- prefix += "readonly ";
-
- QString member_type = parseUiQualifiedId(node->memberType);
-
- if (has_type_modifier)
- member_type = node->typeModifier + "<" + member_type + ">";
-
- addLine(commentFront);
- if (is_readonly && statement.isEmpty()
- && scope().m_bindings.contains(node->name.toString())) {
- m_result += formatLine(prefix + "property " + member_type + " ", false);
-
- scope().m_pendingBinding = true;
- } else {
- addLine(prefix + "property " + member_type + " "
- + node->name+statement + commentBackInline);
- }
- break;
- }
- }
-
- return true;
-}
-
-QString DumpAstVisitor::generateIndent(int indentLevel) const
-{
- return QString(m_indentWidth * indentLevel, m_indentation == Indentation::Tabs ? '\t' : ' ');
-}
-
-QString DumpAstVisitor::formatLine(QString line, bool newline) const
-{
- QString result = generateIndent(m_indentLevel) + line;
- if (newline)
- result += "\n";
-
- return result;
-}
-
-QString DumpAstVisitor::formatPartlyFormatedLines(QString line, bool newline) const
-{
- QString result;
-
- const auto lines = QStringView { line }.split('\n');
- auto it = lines.cbegin();
- const auto endi = lines.cend();
- if (it != endi) {
- result += generateIndent(m_indentLevel) + *it;
- ++it;
- const QString addonIdent = generateIndent(1);
- for (; it != endi; ++it) {
- result += '\n';
- result += addonIdent + *it;
- }
- }
-
- if (newline)
- result += "\n";
-
- return result;
-}
-
-void DumpAstVisitor::addNewLine(bool always) {
- if (!always && m_result.endsWith("\n\n"))
- return;
-
- m_result += "\n";
-}
-
-void DumpAstVisitor::addLine(QString line) {
- // addLine does not support empty lines, use addNewLine(true) for that
- if (line.isEmpty())
- return;
-
- m_result += formatLine(line);
-}
-
-QHash<QString, UiObjectMember*> findBindings(UiObjectMemberList *list) {
- QHash<QString, UiObjectMember*> bindings;
-
- // This relies on RestructureASTVisitor having run beforehand
-
- for (auto *item = list; item != nullptr; item = item->next) {
- switch (item->member->kind) {
- case Node::Kind_UiPublicMember: {
- UiPublicMember *member = cast<UiPublicMember *>(item->member);
-
- if (member->type != UiPublicMember::Property)
- continue;
-
- bindings[member->name.toString()] = nullptr;
-
- break;
- }
- case Node::Kind_UiObjectBinding: {
- UiObjectBinding *binding = cast<UiObjectBinding *>(item->member);
-
- const QString name = parseUiQualifiedId(binding->qualifiedId);
-
- if (bindings.contains(name))
- bindings[name] = binding;
-
- break;
- }
- case Node::Kind_UiArrayBinding: {
- UiArrayBinding *binding = cast<UiArrayBinding *>(item->member);
-
- const QString name = parseUiQualifiedId(binding->qualifiedId);
-
- if (bindings.contains(name))
- bindings[name] = binding;
-
- break;
- }
- case Node::Kind_UiScriptBinding:
- // We can ignore UiScriptBindings since those are actually properly attached to the property
- break;
- }
- }
-
- return bindings;
-}
-
-bool DumpAstVisitor::visit(UiInlineComponent *node)
-{
- m_component_name = node->name.toString();
- return true;
-}
-
-bool DumpAstVisitor::visit(UiObjectDefinition *node) {
- if (scope().m_firstObject) {
- if (scope().m_firstOfAll)
- scope().m_firstOfAll = false;
- else
- addNewLine();
-
- scope().m_firstObject = false;
- }
-
- addLine(getComment(node, Comment::Location::Front));
- addLine(getComment(node, Comment::Location::Front_Inline));
-
- QString component = "";
-
- if (!m_component_name.isEmpty()) {
- component = "component "+m_component_name+": ";
- m_component_name = "";
- }
-
- addLine(component + parseUiQualifiedId(node->qualifiedTypeNameId) + " {");
-
- m_indentLevel++;
-
- ScopeProperties props;
- props.m_bindings = findBindings(node->initializer->members);
- m_scope_properties.push(props);
-
- m_result += getOrphanedComments(node);
-
- return true;
-}
-
-void DumpAstVisitor::endVisit(UiObjectDefinition *node) {
- m_indentLevel--;
-
- m_scope_properties.pop();
-
- bool need_comma = scope().m_inArrayBinding && scope().m_lastInArrayBinding != node;
-
- addLine(need_comma ? "}," : "}");
- addLine(getComment(node, Comment::Location::Back));
- if (!scope().m_inArrayBinding)
- addNewLine();
-}
-
-bool DumpAstVisitor::visit(UiEnumDeclaration *node) {
-
- addNewLine();
-
- addLine(getComment(node, Comment::Location::Front));
- addLine("enum " + node->name + " {");
- m_indentLevel++;
- m_result += getOrphanedComments(node);
-
- return true;
-}
-
-void DumpAstVisitor::endVisit(UiEnumDeclaration *) {
- m_indentLevel--;
- addLine("}");
-
- addNewLine();
-}
-
-bool DumpAstVisitor::visit(UiEnumMemberList *node) {
- for (auto *members = node; members != nullptr; members = members->next) {
-
- addLine(getListItemComment(members->memberToken, Comment::Location::Front));
-
- QString line = members->member.toString();
-
- if (members->valueToken.isValid())
- line += " = "+QString::number(members->value);
-
- if (members->next != nullptr)
- line += ",";
-
- line += getListItemComment(members->memberToken, Comment::Location::Back_Inline);
-
- addLine(line);
- }
-
- return true;
-}
-
-bool DumpAstVisitor::visit(UiScriptBinding *node) {
- if (scope().m_firstBinding) {
- if (scope().m_firstOfAll)
- scope().m_firstOfAll = false;
- else
- addNewLine();
-
- if (parseUiQualifiedId(node->qualifiedId) != "id")
- scope().m_firstBinding = false;
- }
-
- addLine(getComment(node, Comment::Location::Front));
-
- bool multiline = !needsSemicolon(node->statement->kind);
-
- if (multiline) {
- m_indentLevel++;
- }
-
- QString statement = parseStatement(node->statement);
-
- if (multiline) {
- statement = "{\n" + formatLine(statement);
- m_indentLevel--;
- statement += formatLine("}", false);
- }
-
- QString result = parseUiQualifiedId(node->qualifiedId) + ":";
-
- if (!statement.isEmpty())
- result += " "+statement;
- else
- result += ";";
-
- result += getComment(node, Comment::Location::Back_Inline);
-
- addLine(result);
-
- return true;
-}
-
-bool DumpAstVisitor::visit(UiArrayBinding *node) {
- if (!scope().m_pendingBinding && scope().m_firstBinding) {
- if (scope().m_firstOfAll)
- scope().m_firstOfAll = false;
- else
- addNewLine();
-
- scope().m_firstBinding = false;
- }
-
- if (scope().m_pendingBinding) {
- m_result += parseUiQualifiedId(node->qualifiedId)+ ": [\n";
- scope().m_pendingBinding = false;
- } else {
- addLine(getComment(node, Comment::Location::Front));
- addLine(parseUiQualifiedId(node->qualifiedId)+ ": [");
- }
-
- m_indentLevel++;
-
- ScopeProperties props;
- props.m_inArrayBinding = true;
-
- for (auto *item = node->members; item != nullptr; item = item->next) {
- if (item->next == nullptr)
- props.m_lastInArrayBinding = item->member;
- }
-
- m_scope_properties.push(props);
-
- m_result += getOrphanedComments(node);
-
- return true;
-}
-
-void DumpAstVisitor::endVisit(UiArrayBinding *) {
- m_indentLevel--;
- m_scope_properties.pop();
- addLine("]");
-}
-
-bool DumpAstVisitor::visit(FunctionDeclaration *node) {
- if (scope().m_firstFunction) {
- if (scope().m_firstOfAll)
- scope().m_firstOfAll = false;
- else
- addNewLine();
-
- scope().m_firstFunction = false;
- }
-
- addLine(getComment(node, Comment::Location::Front));
-
- QString head = "function";
-
- if (node->isGenerator)
- head += "*";
-
- head += " "+node->name+"("+parseFormalParameterList(node->formals)+")";
-
- if (node->typeAnnotation != nullptr)
- head += " : " + parseType(node->typeAnnotation->type);
-
- head += " {";
-
- addLine(head);
- m_indentLevel++;
-
- return true;
-}
-
-void DumpAstVisitor::endVisit(FunctionDeclaration *node)
-{
- m_result += parseStatementList(node->body);
- m_indentLevel--;
- addLine("}");
- addNewLine();
-}
-
-bool DumpAstVisitor::visit(UiObjectBinding *node) {
- if (!scope().m_pendingBinding && scope().m_firstObject) {
- if (scope().m_firstOfAll)
- scope().m_firstOfAll = false;
- else
- addNewLine();
-
- scope().m_firstObject = false;
- }
-
- QString name = parseUiQualifiedId(node->qualifiedTypeNameId);
-
- QString result = name;
-
- ScopeProperties props;
- props.m_bindings = findBindings(node->initializer->members);
- m_scope_properties.push(props);
-
- if (node->hasOnToken)
- result += " on "+parseUiQualifiedId(node->qualifiedId);
- else
- result.prepend(parseUiQualifiedId(node->qualifiedId) + ": ");
-
- if (scope().m_pendingBinding) {
- m_result += result + " {\n";
-
- scope().m_pendingBinding = false;
- } else {
- addNewLine();
- addLine(getComment(node, Comment::Location::Front));
- addLine(getComment(node, Comment::Location::Front_Inline));
- addLine(result + " {");
- }
-
- m_indentLevel++;
-
- return true;
-}
-
-void DumpAstVisitor::endVisit(UiObjectBinding *node) {
- m_indentLevel--;
- m_scope_properties.pop();
-
- addLine("}");
- addLine(getComment(node, Comment::Location::Back));
-
- addNewLine();
-}
-
-bool DumpAstVisitor::visit(UiImport *node) {
- scope().m_firstOfAll = false;
-
- addLine(getComment(node, Comment::Location::Front));
-
- QString result = "import ";
-
- if (!node->fileName.isEmpty())
- result += escapeString(node->fileName.toString());
- else
- result += parseUiQualifiedId(node->importUri);
-
- if (node->version) {
- const auto version = node->version->version;
-
- if (version.hasMajorVersion()) {
- result += " " + QString::number(version.majorVersion());
-
- if (version.hasMinorVersion())
- result += "." + QString::number(version.minorVersion());
- }
- }
-
- if (node->asToken.isValid()) {
- result +=" as " + node->importId;
- }
-
- result += getComment(node, Comment::Location::Back_Inline);
-
- addLine(result);
-
- return true;
-}
-
-bool DumpAstVisitor::visit(UiPragma *node) {
- scope().m_firstOfAll = false;
-
- addLine(getComment(node, Comment::Location::Front));
- QString result = "pragma "+ node->name;
- result += getComment(node, Comment::Location::Back_Inline);
-
- addLine(result);
-
- return true;
-}
-
-bool DumpAstVisitor::visit(UiAnnotation *node)
-{
- if (scope().m_firstObject) {
- if (scope().m_firstOfAll)
- scope().m_firstOfAll = false;
- else
- addNewLine();
-
- scope().m_firstObject = false;
- }
-
- addLine(getComment(node, Comment::Location::Front));
- addLine(QLatin1String("@") + parseUiQualifiedId(node->qualifiedTypeNameId) + " {");
-
- m_indentLevel++;
-
- ScopeProperties props;
- props.m_bindings = findBindings(node->initializer->members);
- m_scope_properties.push(props);
-
- m_result += getOrphanedComments(node);
-
- return true;
-}
-
-void DumpAstVisitor::endVisit(UiAnnotation *node) {
- m_indentLevel--;
-
- m_scope_properties.pop();
-
- addLine("}");
- addLine(getComment(node, Comment::Location::Back));
-}
diff --git a/tools/qmlformat/dumpastvisitor.h b/tools/qmlformat/dumpastvisitor.h
deleted file mode 100644
index 657592f403..0000000000
--- a/tools/qmlformat/dumpastvisitor.h
+++ /dev/null
@@ -1,161 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef DUMPAST_H
-#define DUMPAST_H
-
-#include <QtQml/private/qqmljsastvisitor_p.h>
-#include <QtQml/private/qqmljsast_p.h>
-
-#include <QHash>
-#include <QStack>
-
-#include "commentastvisitor.h"
-
-using namespace QQmlJS::AST;
-using namespace QQmlJS;
-
-class DumpAstVisitor : protected Visitor
-{
-public:
- enum Indentation { Tabs, Spaces };
-
- DumpAstVisitor(QQmlJS::Engine *engine, Node *rootNode, CommentAstVisitor *comment,
- int indentWidth, Indentation indentation);
-
- QString toString() const { return m_result; }
-
- bool preVisit(Node *) override;
-
- bool visit(UiScriptBinding *node) override;
-
- bool visit(UiArrayBinding *node) override;
- void endVisit(UiArrayBinding *node) override;
-
- bool visit(UiObjectBinding *node) override;
- void endVisit(UiObjectBinding *node) override;
-
- bool visit(FunctionDeclaration *node) override;
- void endVisit(FunctionDeclaration *node) override;
-
- bool visit(UiInlineComponent *node) override;
-
- bool visit(UiObjectDefinition *node) override;
- void endVisit(UiObjectDefinition *node) override;
-
- bool visit(UiEnumDeclaration *node) override;
- void endVisit(UiEnumDeclaration *node) override;
-
- bool visit(UiEnumMemberList *node) override;
- bool visit(UiPublicMember *node) override;
- bool visit(UiImport *node) override;
- bool visit(UiPragma *node) override;
-
- bool visit(UiAnnotation *node) override;
- void endVisit(UiAnnotation *node) override;
-
- void throwRecursionDepthError() override {}
-
- bool error() const { return m_error; }
-private:
- struct ScopeProperties {
- bool m_firstOfAll = true;
- bool m_firstSignal = true;
- bool m_firstProperty = true;
- bool m_firstBinding = true;
- bool m_firstObject = true;
- bool m_firstFunction = true;
- bool m_inArrayBinding = false;
- bool m_pendingBinding = false;
-
- UiObjectMember* m_lastInArrayBinding = nullptr;
- QHash<QString, UiObjectMember*> m_bindings;
- };
-
- QString generateIndent(int indentLevel) const;
- QString formatLine(QString line, bool newline = true) const;
- QString formatPartlyFormatedLines(QString line, bool newline = true) const;
-
- QString formatComment(const Comment &comment) const;
-
- QString getComment(Node *node, Comment::Location location) const;
- QString getListItemComment(SourceLocation srcLocation, Comment::Location location) const;
-
- void addNewLine(bool always = false);
- void addLine(QString line);
-
- QString getOrphanedComments(Node *node) const;
-
- QString parseStatement(Statement *statement, bool blockHasNext = false,
- bool blockAllowBraceless = false);
- QString parseStatementList(StatementList *list);
-
- QString parseExpression(ExpressionNode *expression);
-
- QString parsePatternElement(PatternElement *element, bool scope = true);
- QString parsePatternElementList(PatternElementList *element);
-
- QString parsePatternProperty(PatternProperty *property);
- QString parsePatternPropertyList(PatternPropertyList *list);
-
- QString parseArgumentList(ArgumentList *list);
-
- QString parseUiParameterList(UiParameterList *list);
-
- QString parseVariableDeclarationList(VariableDeclarationList *list);
-
- QString parseCaseBlock(CaseBlock *block);
- QString parseBlock(Block *block, bool hasNext, bool allowBraceless);
-
- QString parseExportsList(ExportsList *list);
- QString parseExportSpecifier(ExportSpecifier *specifier);
-
- QString parseFormalParameterList(FormalParameterList *list);
-
- QString parseType(Type *type);
-
- QString parseFunctionExpression(FunctionExpression *expression, bool omitFunction = false);
-
- ScopeProperties& scope() { return m_scope_properties.top(); }
-
- int m_indentLevel = 0;
-
- bool m_error = false;
- bool m_blockNeededBraces = false;
-
- QStack<ScopeProperties> m_scope_properties;
-
- QString m_result = "";
- QString m_component_name = "";
- QQmlJS::Engine *m_engine;
- CommentAstVisitor *m_comment;
- int m_indentWidth;
- Indentation m_indentation;
-};
-
-#endif // DUMPAST_H
diff --git a/tools/qmlformat/main.cpp b/tools/qmlformat/main.cpp
deleted file mode 100644
index da3c1772fb..0000000000
--- a/tools/qmlformat/main.cpp
+++ /dev/null
@@ -1,293 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QCoreApplication>
-#include <QFile>
-#include <QTextStream>
-
-#include <QtQml/private/qqmljslexer_p.h>
-#include <QtQml/private/qqmljsparser_p.h>
-#include <QtQml/private/qqmljsengine_p.h>
-#include <QtQml/private/qqmljsastvisitor_p.h>
-#include <QtQml/private/qqmljsast_p.h>
-
-#if QT_CONFIG(commandlineparser)
-#include <QCommandLineParser>
-#endif
-
-#include "commentastvisitor.h"
-#include "dumpastvisitor.h"
-#include "restructureastvisitor.h"
-
-struct Options
-{
- bool verbose = false;
- bool inplace = false;
- bool force = false;
- bool tabs = false;
- bool valid = false;
-
- int indentWidth = 4;
- bool indentWidthSet = false;
- QString newline = "native";
-
- QStringList files;
- QStringList arguments;
- QStringList errors;
-};
-
-bool parseFile(const QString &filename, const Options &options)
-{
- QFile file(filename);
-
- if (!file.open(QIODevice::Text | QIODevice::ReadOnly)) {
- qWarning().noquote() << "Failed to open" << filename << "for reading.";
- return false;
- }
-
- QString code = QString::fromUtf8(file.readAll());
- file.close();
-
- QQmlJS::Engine engine;
- QQmlJS::Lexer lexer(&engine);
-
- lexer.setCode(code, 1, true);
- QQmlJS::Parser parser(&engine);
-
- bool success = parser.parse();
-
- if (!success) {
- const auto diagnosticMessages = parser.diagnosticMessages();
- for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) {
- qWarning().noquote() << QString::fromLatin1("%1:%2 : %3")
- .arg(filename).arg(m.loc.startLine).arg(m.message);
- }
-
- qWarning().noquote() << "Failed to parse" << filename;
- return false;
- }
-
- // Try to attach comments to AST nodes
- CommentAstVisitor comment(&engine, parser.rootNode());
-
- if (options.verbose)
- qWarning().noquote() << comment.attachedComments().size() << "comment(s) attached.";
-
- if (options.verbose) {
- int orphaned = 0;
-
- for (const auto& orphanList : comment.orphanComments().values())
- orphaned += orphanList.size();
-
- qWarning().noquote() << orphaned << "comments are orphans.";
- }
-
- // Do the actual restructuring
- RestructureAstVisitor restructure(parser.rootNode());
-
- // Turn AST back into source code
- if (options.verbose)
- qWarning().noquote() << "Dumping" << filename;
-
- DumpAstVisitor dump(
- &engine, parser.rootNode(), &comment, options.tabs ? 1 : options.indentWidth,
- options.tabs ? DumpAstVisitor::Indentation::Tabs : DumpAstVisitor::Indentation::Spaces);
-
- QString dumpCode = dump.toString();
-
- lexer.setCode(dumpCode, 1, true);
-
- bool dumpSuccess = parser.parse();
-
- if (!dumpSuccess) {
- if (options.verbose) {
- const auto diagnosticMessages = parser.diagnosticMessages();
- for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) {
- qWarning().noquote() << QString::fromLatin1("<formatted>:%2 : %3")
- .arg(m.loc.startLine).arg(m.message);
- }
- }
-
- qWarning().noquote() << "Failed to parse formatted code.";
- }
-
- if (dump.error() || !dumpSuccess) {
- if (options.force) {
- qWarning().noquote() << "An error has occurred. The output may not be reliable.";
- } else {
- qWarning().noquote() << "An error has occurred. Aborting.";
- return false;
- }
- }
-
- const bool native = options.newline == "native";
-
- if (!native) {
- if (options.newline == "macos") {
- dumpCode = dumpCode.replace("\n", "\r");
- } else if (options.newline == "windows") {
- dumpCode = dumpCode.replace("\n", "\r\n");
- } else if (options.newline == "unix") {
- // Nothing needs to be done for unix line-endings
- } else {
- qWarning().noquote() << "Unknown line ending type" << options.newline;
- return false;
- }
- }
-
- if (options.inplace) {
- if (options.verbose)
- qWarning().noquote() << "Writing to file" << filename;
-
- if (!file.open(native ? QIODevice::WriteOnly | QIODevice::Text : QIODevice::WriteOnly)) {
- qWarning().noquote() << "Failed to open" << filename << "for writing";
- return false;
- }
-
- file.write(dumpCode.toUtf8());
- file.close();
- } else {
- QFile out;
- out.open(stdout, QIODevice::WriteOnly);
- out.write(dumpCode.toUtf8());
- }
-
- return true;
-}
-
-Options buildCommandLineOptions(const QCoreApplication &app)
-{
-#if QT_CONFIG(commandlineparser)
- QCommandLineParser parser;
- parser.setApplicationDescription("Formats QML files according to the QML Coding Conventions.");
- parser.addHelpOption();
- parser.addVersionOption();
-
- parser.addOption(QCommandLineOption({"V", "verbose"},
- QStringLiteral("Verbose mode. Outputs more detailed information.")));
-
- parser.addOption(QCommandLineOption({"i", "inplace"},
- QStringLiteral("Edit file in-place instead of outputting to stdout.")));
-
- parser.addOption(QCommandLineOption({"f", "force"},
- QStringLiteral("Continue even if an error has occurred.")));
-
- parser.addOption(
- QCommandLineOption({ "t", "tabs" }, QStringLiteral("Use tabs instead of spaces.")));
-
- parser.addOption(QCommandLineOption({ "w", "indent-width" },
- QStringLiteral("How many spaces are used when indenting."),
- "width", "4"));
-
- parser.addOption(QCommandLineOption(
- { "F", "files" }, QStringLiteral("Format all files listed in file, in-place"), "file"));
-
- parser.addOption(QCommandLineOption({"l", "newline"},
- QStringLiteral("Override the new line format to use (native macos unix windows)."),
- "newline", "native"));
-
- parser.addPositionalArgument("filenames", "files to be processed by qmlformat");
-
- parser.process(app);
-
- bool indentWidthOkay = false;
- const int indentWidth = parser.value("indent-width").toInt(&indentWidthOkay);
- if (!indentWidthOkay) {
- Options options;
- options.errors.push_back("Error: Invalid value passed to -w");
- return options;
- }
-
- QStringList files;
- QFile file(parser.value("files"));
- file.open(QIODevice::Text | QIODevice::ReadOnly);
- if (file.isOpen()) {
- QTextStream in(&file);
- while (!in.atEnd()) {
- QString file = in.readLine();
-
- if (file.isEmpty())
- continue;
-
- files.push_back(file);
- }
- }
-
- Options options;
- options.verbose = parser.isSet("verbose");
- options.inplace = parser.isSet("inplace");
- options.force = parser.isSet("force");
- options.tabs = parser.isSet("tabs");
- options.valid = true;
-
- options.indentWidth = indentWidth;
- options.indentWidthSet = parser.isSet("indent-width");
- options.newline = parser.value("newline");
- options.files = files;
- options.arguments = parser.positionalArguments();
- return options;
-#else
- return Options {};
-#endif
-}
-
-int main(int argc, char *argv[])
-{
- QCoreApplication app(argc, argv);
- QCoreApplication::setApplicationName("qmlformat");
- QCoreApplication::setApplicationVersion("1.0");
-
- const auto options = buildCommandLineOptions(app);
- if (!options.valid) {
- for (const auto &error : options.errors) {
- qWarning().noquote() << error;
- }
-
- return -1;
- }
-
- bool success = true;
- if (!options.files.isEmpty()) {
- if (!options.arguments.isEmpty())
- qWarning() << "Warning: Positional arguments are ignored when -F is used";
-
- for (const QString &file : options.files) {
- Q_ASSERT(!file.isEmpty());
-
- if (!parseFile(file, options))
- success = false;
- }
- } else {
- for (const QString &file : options.arguments) {
- if (!parseFile(file, options))
- success = false;
- }
- }
-
- return success ? 0 : 1;
-}
diff --git a/tools/qmlformat/qmlformat.cpp b/tools/qmlformat/qmlformat.cpp
new file mode 100644
index 0000000000..e26a6412c9
--- /dev/null
+++ b/tools/qmlformat/qmlformat.cpp
@@ -0,0 +1,390 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <QCoreApplication>
+#include <QFile>
+#include <QTextStream>
+
+#include <QtQml/private/qqmljslexer_p.h>
+#include <QtQml/private/qqmljsparser_p.h>
+#include <QtQml/private/qqmljsengine_p.h>
+#include <QtQml/private/qqmljsastvisitor_p.h>
+#include <QtQml/private/qqmljsast_p.h>
+#include <QtQmlDom/private/qqmldomitem_p.h>
+#include <QtQmlDom/private/qqmldomexternalitems_p.h>
+#include <QtQmlDom/private/qqmldomtop_p.h>
+#include <QtQmlDom/private/qqmldomoutwriter_p.h>
+
+#if QT_CONFIG(commandlineparser)
+# include <QCommandLineParser>
+#endif
+
+#include <QtQmlToolingSettings/private/qqmltoolingsettings_p.h>
+
+
+using namespace QQmlJS::Dom;
+
+struct Options
+{
+ bool verbose = false;
+ bool inplace = false;
+ bool force = false;
+ bool tabs = false;
+ bool valid = false;
+ bool normalize = false;
+ bool ignoreSettings = false;
+ bool writeDefaultSettings = false;
+ bool objectsSpacing = false;
+ bool functionsSpacing = false;
+
+ int indentWidth = 4;
+ bool indentWidthSet = false;
+ QString newline = "native";
+
+ QStringList files;
+ QStringList arguments;
+ QStringList errors;
+};
+
+// TODO refactor
+// Move out to the LineWriterOptions class / helper
+static LineWriterOptions composeLwOptions(const Options &options, QStringView code)
+{
+ LineWriterOptions lwOptions;
+ lwOptions.formatOptions.indentSize = options.indentWidth;
+ lwOptions.formatOptions.useTabs = options.tabs;
+ lwOptions.updateOptions = LineWriterOptions::Update::None;
+ if (options.newline == "native") {
+ // find out current line endings...
+ int newlineIndex = code.indexOf(QChar(u'\n'));
+ int crIndex = code.indexOf(QChar(u'\r'));
+ if (newlineIndex >= 0) {
+ if (crIndex >= 0) {
+ if (crIndex + 1 == newlineIndex)
+ lwOptions.lineEndings = LineWriterOptions::LineEndings::Windows;
+ else
+ qWarning().noquote() << "Invalid line ending in file, using default";
+
+ } else {
+ lwOptions.lineEndings = LineWriterOptions::LineEndings::Unix;
+ }
+ } else if (crIndex >= 0) {
+ lwOptions.lineEndings = LineWriterOptions::LineEndings::OldMacOs;
+ } else {
+ qWarning().noquote() << "Unknown line ending in file, using default";
+ }
+ } else if (options.newline == "macos") {
+ lwOptions.lineEndings = LineWriterOptions::LineEndings::OldMacOs;
+ } else if (options.newline == "windows") {
+ lwOptions.lineEndings = LineWriterOptions::LineEndings::Windows;
+ } else if (options.newline == "unix") {
+ lwOptions.lineEndings = LineWriterOptions::LineEndings::Unix;
+ } else {
+ qWarning().noquote() << "Unknown line ending type" << options.newline << ", using default";
+ }
+
+ if (options.normalize)
+ lwOptions.attributesSequence = LineWriterOptions::AttributesSequence::Normalize;
+ else
+ lwOptions.attributesSequence = LineWriterOptions::AttributesSequence::Preserve;
+
+ lwOptions.objectsSpacing = options.objectsSpacing;
+ lwOptions.functionsSpacing = options.functionsSpacing;
+ return lwOptions;
+}
+
+static void logParsingErrors(const DomItem &fileItem, const QString &filename)
+{
+ fileItem.iterateErrors(
+ [](const DomItem &, const ErrorMessage &msg) {
+ errorToQDebug(msg);
+ return true;
+ },
+ true);
+ qWarning().noquote() << "Failed to parse" << filename;
+}
+
+// TODO
+// refactor this workaround. ExternalOWningItem is not recognized as an owning type
+// in ownerAs.
+static std::shared_ptr<ExternalOwningItem> getFileItemOwner(const DomItem &fileItem)
+{
+ std::shared_ptr<ExternalOwningItem> filePtr = nullptr;
+ switch (fileItem.internalKind()) {
+ case DomType::JsFile:
+ filePtr = fileItem.ownerAs<JsFile>();
+ break;
+ default:
+ filePtr = fileItem.ownerAs<QmlFile>();
+ break;
+ }
+ return filePtr;
+}
+
+// TODO refactor
+// Introduce better encapsulation and separation of concerns and move to DOM API
+// returns a DomItem corresponding to the loaded file and bool indicating the validity of the file
+static std::pair<DomItem, bool> parse(const QString &filename)
+{
+ auto envPtr =
+ DomEnvironment::create(QStringList(),
+ QQmlJS::Dom::DomEnvironment::Option::SingleThreaded
+ | QQmlJS::Dom::DomEnvironment::Option::NoDependencies);
+ // placeholder for a node
+ // containing metadata (ExternalItemInfo) about the loaded file
+ DomItem fMetadataItem;
+ envPtr->loadFile(FileToLoad::fromFileSystem(envPtr, filename),
+ // callback called when everything is loaded that receives the
+ // loaded external file pair (path, oldValue, newValue)
+ [&fMetadataItem](Path, const DomItem &, const DomItem &extItemInfo) {
+ fMetadataItem = extItemInfo;
+ });
+ auto fItem = fMetadataItem.fileObject();
+ auto filePtr = getFileItemOwner(fItem);
+ return { fItem, filePtr && filePtr->isValid() };
+}
+
+static bool parseFile(const QString &filename, const Options &options)
+{
+ const auto [fileItem, validFile] = parse(filename);
+ if (!validFile) {
+ logParsingErrors(fileItem, filename);
+ return false;
+ }
+
+ // Turn AST back into source code
+ if (options.verbose)
+ qWarning().noquote() << "Dumping" << filename;
+
+ const auto &code = getFileItemOwner(fileItem)->code();
+ auto lwOptions = composeLwOptions(options, code);
+ WriteOutChecks checks = WriteOutCheck::Default;
+ //Disable writeOutChecks for some usecases
+ if (options.force ||
+ code.size() > 32000 ||
+ fileItem.internalKind() == DomType::JsFile) {
+ checks = WriteOutCheck::None;
+ }
+
+ bool res = false;
+ if (options.inplace) {
+ if (options.verbose)
+ qWarning().noquote() << "Writing to file" << filename;
+ FileWriter fw;
+ const unsigned numberOfBackupFiles = 0;
+ res = fileItem.writeOut(filename, numberOfBackupFiles, lwOptions, &fw, checks);
+ } else {
+ QFile out;
+ if (out.open(stdout, QIODevice::WriteOnly)) {
+ LineWriter lw([&out](QStringView s) { out.write(s.toUtf8()); }, filename, lwOptions);
+ OutWriter ow(lw);
+ res = fileItem.writeOutForFile(ow, checks);
+ ow.flush();
+ } else {
+ res = false;
+ }
+ }
+ return res;
+}
+
+Options buildCommandLineOptions(const QCoreApplication &app)
+{
+#if QT_CONFIG(commandlineparser)
+ QCommandLineParser parser;
+ parser.setApplicationDescription("Formats QML files according to the QML Coding Conventions.");
+ parser.addHelpOption();
+ parser.addVersionOption();
+
+ parser.addOption(
+ QCommandLineOption({ "V", "verbose" },
+ QStringLiteral("Verbose mode. Outputs more detailed information.")));
+
+ QCommandLineOption writeDefaultsOption(
+ QStringList() << "write-defaults",
+ QLatin1String("Writes defaults settings to .qmlformat.ini and exits (Warning: This "
+ "will overwrite any existing settings and comments!)"));
+ parser.addOption(writeDefaultsOption);
+
+ QCommandLineOption ignoreSettings(QStringList() << "ignore-settings",
+ QLatin1String("Ignores all settings files and only takes "
+ "command line options into consideration"));
+ parser.addOption(ignoreSettings);
+
+ parser.addOption(QCommandLineOption(
+ { "i", "inplace" },
+ QStringLiteral("Edit file in-place instead of outputting to stdout.")));
+
+ parser.addOption(QCommandLineOption({ "f", "force" },
+ QStringLiteral("Continue even if an error has occurred.")));
+
+ parser.addOption(
+ QCommandLineOption({ "t", "tabs" }, QStringLiteral("Use tabs instead of spaces.")));
+
+ parser.addOption(QCommandLineOption({ "w", "indent-width" },
+ QStringLiteral("How many spaces are used when indenting."),
+ "width", "4"));
+
+ parser.addOption(QCommandLineOption({ "n", "normalize" },
+ QStringLiteral("Reorders the attributes of the objects "
+ "according to the QML Coding Guidelines.")));
+
+ parser.addOption(QCommandLineOption(
+ { "F", "files" }, QStringLiteral("Format all files listed in file, in-place"), "file"));
+
+ parser.addOption(QCommandLineOption(
+ { "l", "newline" },
+ QStringLiteral("Override the new line format to use (native macos unix windows)."),
+ "newline", "native"));
+
+ parser.addOption(QCommandLineOption(QStringList() << "objects-spacing", QStringLiteral("Ensure spaces between objects (only works with normalize option).")));
+
+ parser.addOption(QCommandLineOption(QStringList() << "functions-spacing", QStringLiteral("Ensure spaces between functions (only works with normalize option).")));
+
+ parser.addPositionalArgument("filenames", "files to be processed by qmlformat");
+
+ parser.process(app);
+
+ if (parser.isSet(writeDefaultsOption)) {
+ Options options;
+ options.writeDefaultSettings = true;
+ options.valid = true;
+ return options;
+ }
+
+ bool indentWidthOkay = false;
+ const int indentWidth = parser.value("indent-width").toInt(&indentWidthOkay);
+ if (!indentWidthOkay) {
+ Options options;
+ options.errors.push_back("Error: Invalid value passed to -w");
+ return options;
+ }
+
+ QStringList files;
+ if (!parser.value("files").isEmpty()) {
+ QFile file(parser.value("files"));
+ if (file.open(QIODevice::Text | QIODevice::ReadOnly)) {
+ QTextStream in(&file);
+ while (!in.atEnd()) {
+ QString file = in.readLine();
+
+ if (file.isEmpty())
+ continue;
+
+ files.push_back(file);
+ }
+ }
+ }
+
+ Options options;
+ options.verbose = parser.isSet("verbose");
+ options.inplace = parser.isSet("inplace");
+ options.force = parser.isSet("force");
+ options.tabs = parser.isSet("tabs");
+ options.normalize = parser.isSet("normalize");
+ options.ignoreSettings = parser.isSet("ignore-settings");
+ options.objectsSpacing = parser.isSet("objects-spacing");
+ options.functionsSpacing = parser.isSet("functions-spacing");
+ options.valid = true;
+
+ options.indentWidth = indentWidth;
+ options.indentWidthSet = parser.isSet("indent-width");
+ options.newline = parser.value("newline");
+ options.files = files;
+ options.arguments = parser.positionalArguments();
+ return options;
+#else
+ return Options {};
+#endif
+}
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication app(argc, argv);
+ QCoreApplication::setApplicationName("qmlformat");
+ QCoreApplication::setApplicationVersion(QT_VERSION_STR);
+
+ QQmlToolingSettings settings(QLatin1String("qmlformat"));
+
+ const QString &useTabsSetting = QStringLiteral("UseTabs");
+ settings.addOption(useTabsSetting);
+
+ const QString &indentWidthSetting = QStringLiteral("IndentWidth");
+ settings.addOption(indentWidthSetting, 4);
+
+ const QString &normalizeSetting = QStringLiteral("NormalizeOrder");
+ settings.addOption(normalizeSetting);
+
+ const QString &newlineSetting = QStringLiteral("NewlineType");
+ settings.addOption(newlineSetting, QStringLiteral("native"));
+
+ const QString &objectsSpacingSetting = QStringLiteral("ObjectsSpacing");
+ settings.addOption(objectsSpacingSetting);
+
+ const QString &functionsSpacingSetting = QStringLiteral("FunctionsSpacing");
+ settings.addOption(functionsSpacingSetting);
+
+ const auto options = buildCommandLineOptions(app);
+ if (!options.valid) {
+ for (const auto &error : options.errors) {
+ qWarning().noquote() << error;
+ }
+
+ return -1;
+ }
+
+ if (options.writeDefaultSettings)
+ return settings.writeDefaults() ? 0 : -1;
+
+ auto getSettings = [&](const QString &file, Options options) {
+ // Perform formatting inplace if --files option is set.
+ if (!options.files.isEmpty())
+ options.inplace = true;
+
+ if (options.ignoreSettings || !settings.search(file))
+ return options;
+
+ Options perFileOptions = options;
+
+ // Allow for tab settings to be overwritten by the command line
+ if (!options.indentWidthSet) {
+ if (settings.isSet(indentWidthSetting))
+ perFileOptions.indentWidth = settings.value(indentWidthSetting).toInt();
+ if (settings.isSet(useTabsSetting))
+ perFileOptions.tabs = settings.value(useTabsSetting).toBool();
+ }
+
+ if (settings.isSet(normalizeSetting))
+ perFileOptions.normalize = settings.value(normalizeSetting).toBool();
+
+ if (settings.isSet(newlineSetting))
+ perFileOptions.newline = settings.value(newlineSetting).toString();
+
+ if (settings.isSet(objectsSpacingSetting))
+ perFileOptions.objectsSpacing = settings.value(objectsSpacingSetting).toBool();
+
+ if (settings.isSet(functionsSpacingSetting))
+ perFileOptions.functionsSpacing = settings.value(functionsSpacingSetting).toBool();
+
+ return perFileOptions;
+ };
+
+ bool success = true;
+ if (!options.files.isEmpty()) {
+ if (!options.arguments.isEmpty())
+ qWarning() << "Warning: Positional arguments are ignored when -F is used";
+
+ for (const QString &file : options.files) {
+ Q_ASSERT(!file.isEmpty());
+
+ if (!parseFile(file, getSettings(file, options)))
+ success = false;
+ }
+ } else {
+ for (const QString &file : options.arguments) {
+ if (!parseFile(file, getSettings(file, options)))
+ success = false;
+ }
+ }
+
+ return success ? 0 : 1;
+}
diff --git a/tools/qmlformat/restructureastvisitor.cpp b/tools/qmlformat/restructureastvisitor.cpp
deleted file mode 100644
index 45957230d8..0000000000
--- a/tools/qmlformat/restructureastvisitor.cpp
+++ /dev/null
@@ -1,194 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "restructureastvisitor.h"
-
-#include <QList>
-
-RestructureAstVisitor::RestructureAstVisitor(Node *rootNode)
-{
- rootNode->accept(this);
-}
-
-template<typename T>
-static QList<T *> findKind(UiObjectMemberList *list)
-{
- QList<T *> members;
- for (auto *item = list; item != nullptr; item = item->next) {
- if (cast<T *>(item->member) != nullptr)
- members.append(cast<T *>(item->member));
- }
-
- return members;
-}
-
-template<typename T>
-static QList<T *> findKind(UiHeaderItemList *list)
-{
- QList<T *> members;
- for (auto *item = list; item != nullptr; item = item->next) {
- if (cast<T *>(item->headerItem) != nullptr)
- members.append(cast<T *>(item->headerItem));
- }
-
- return members;
-}
-
-static QString parseUiQualifiedId(UiQualifiedId *id)
-{
- QString name = id->name.toString();
- for (auto *item = id->next; item != nullptr; item = item->next) {
- name += "." + item->name;
- }
-
- return name;
-}
-
-void RestructureAstVisitor::endVisit(UiObjectMemberList *node)
-{
- QList<UiObjectMember*> correctOrder;
-
- QList<UiScriptBinding*> largeScriptBinding;
-
- UiObjectMember *states = nullptr;
- UiObjectMember *transitions = nullptr;
-
- auto enumDeclarations = findKind<UiEnumDeclaration>(node);
- auto scriptBindings = findKind<UiScriptBinding>(node);
- auto arrayBindings = findKind<UiArrayBinding>(node);
- auto publicMembers = findKind<UiPublicMember>(node);
- auto sourceElements = findKind<UiSourceElement>(node);
- auto objectDefinitions = findKind<UiObjectDefinition>(node);
-
- // Look for transitions and states
- for (auto *binding : findKind<UiObjectBinding>(node)) {
- const QString name = parseUiQualifiedId(binding->qualifiedId);
-
- if (name == "transitions")
- transitions = binding;
- else if (name == "states")
- states = binding;
- }
-
- for (auto it = arrayBindings.begin(); it != arrayBindings.end();) {
- const QString name = parseUiQualifiedId((*it)->qualifiedId);
-
- if (name == "transitions") {
- transitions = *it;
- it = arrayBindings.erase(it);
- } else if (name == "states") {
- states = *it;
- it = arrayBindings.erase(it);
- } else {
- it++;
- }
- }
-
- // Find large script bindings
- for (auto it = scriptBindings.begin(); it != scriptBindings.end();) {
- // A binding is considered large if it uses a block
- if ((*it)->statement->kind != Node::Kind_Block) {
- it++;
- continue;
- }
-
- largeScriptBinding.push_back(*it);
- it = scriptBindings.erase(it);
- }
-
- // This structure is based on https://doc.qt.io/qt-5/qml-codingconventions.html
-
- // 1st id
- for (auto *binding : scriptBindings) {
- if (parseUiQualifiedId(binding->qualifiedId) == "id") {
- correctOrder.append(binding);
-
- scriptBindings.removeOne(binding);
- break;
- }
- }
-
- // 2nd enums
- for (auto *enumDeclaration : enumDeclarations)
- correctOrder.append(enumDeclaration);
-
- // 3rd property declarations
- for (auto *publicMember : publicMembers) {
- if (publicMember->type != UiPublicMember::Property)
- continue;
-
- correctOrder.append(publicMember);
- }
-
- // 4th signals
- for (auto *publicMember : publicMembers) {
- if (publicMember->type != UiPublicMember::Signal)
- continue;
-
- correctOrder.append(publicMember);
- }
-
- // 5th functions
- for (auto *source : sourceElements)
- correctOrder.append(source);
-
- // 6th properties
- // small script bindings...
- for (auto *binding : scriptBindings)
- correctOrder.append(binding);
-
- // ...then large ones
- for (auto *binding : largeScriptBinding)
- correctOrder.append(binding);
-
- for (auto *binding : arrayBindings)
- correctOrder.append(binding);
-
- // 7th child objects
- for (auto *objectDefinition : objectDefinitions)
- correctOrder.append(objectDefinition);
-
- // 8th all the rest
- for (auto *item = node; item != nullptr; item = item->next) {
- if (!correctOrder.contains(item->member))
- correctOrder.append(item->member);
- }
-
- // 9th states and transitions
- if (states != nullptr)
- correctOrder.append(states);
-
- if (transitions != nullptr)
- correctOrder.append(transitions);
-
- // Rebuild member list from correctOrder
- for (auto *item = node; item != nullptr; item = item->next) {
- item->member = correctOrder.front();
- correctOrder.pop_front();
- }
-}
diff --git a/tools/qmlformat/restructureastvisitor.h b/tools/qmlformat/restructureastvisitor.h
deleted file mode 100644
index 7b3573300f..0000000000
--- a/tools/qmlformat/restructureastvisitor.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef RESTRUCTUREASTVISITOR_H
-#define RESTRUCTUREASTVISITOR_H
-
-#include <QtQml/private/qqmljsastvisitor_p.h>
-#include <QtQml/private/qqmljsast_p.h>
-
-using namespace QQmlJS::AST;
-
-class RestructureAstVisitor : protected Visitor
-{
-public:
- RestructureAstVisitor(Node *rootNode);
-
- void throwRecursionDepthError() override {}
-
- void endVisit(UiObjectMemberList *node) override;
-};
-
-#endif // RESTRUCTUREASTVISITOR_H
diff --git a/tools/qmlimportscanner/.prev_CMakeLists.txt b/tools/qmlimportscanner/.prev_CMakeLists.txt
deleted file mode 100644
index 7d56f6b75a..0000000000
--- a/tools/qmlimportscanner/.prev_CMakeLists.txt
+++ /dev/null
@@ -1,62 +0,0 @@
-# Generated from qmlimportscanner.pro.
-
-#####################################################################
-## qmlimportscanner Tool:
-#####################################################################
-
-qt_get_tool_target_name(target_name qmlimportscanner)
-qt_internal_add_tool(${target_name}
- TARGET_DESCRIPTION "QML Import Scanner"
- SOURCES
- main.cpp
- DEFINES
- QT_NO_CAST_FROM_ASCII
- QT_NO_CAST_TO_ASCII
- PUBLIC_LIBRARIES
- Qt::QmlCompilerPrivate
- Qt::QmlDevToolsPrivate
-)
-
-#### Keys ignored in scope 1:.:.:qmlimportscanner.pro:<TRUE>:
-# CMAKE_BIN_DIR = "$$cmakeRelativePath($$[QT_HOST_BINS], $$[QT_INSTALL_PREFIX])"
-# CMAKE_DEBUG_TYPE = <EMPTY>
-# CMAKE_QML_DIR = "$$cmakeRelativePath($$[QT_INSTALL_QML], $$[QT_INSTALL_PREFIX])"
-# CMAKE_QML_PLUGIN_SUFFIX_RELEASE = <EMPTY>
-# CMAKE_RELEASE_TYPE = <EMPTY>
-# QMAKE_TARGET_DESCRIPTION = "QML" "Import" "Scanner"
-# _OPTION = "host_build"
-
-## Scopes:
-#####################################################################
-
-#### Keys ignored in scope 2:.:.:qmlimportscanner.pro:CMAKE_BIN_DIR___contains___^\\.\\./._x_:
-# CMAKE_BIN_DIR = "$$[QT_HOST_BINS]/"
-# CMAKE_BIN_DIR_IS_ABSOLUTE = "True"
-
-#### Keys ignored in scope 3:.:.:qmlimportscanner.pro:CMAKE_QML_DIR___contains___^\\.\\./._x_:
-# CMAKE_QML_DIR = "$$[QT_INSTALL_QML]/"
-# CMAKE_QML_DIR_IS_ABSOLUTE = "True"
-
-#### Keys ignored in scope 4:.:.:qmlimportscanner.pro:static OR staticlib:
-# CMAKE_STATIC_TYPE = "true"
-
-#### Keys ignored in scope 5:.:.:qmlimportscanner.pro:WIN32:
-# CMAKE_QML_PLUGIN_SUFFIX_DEBUG = "d"
-
-#### Keys ignored in scope 7:.:.:qmlimportscanner.pro:APPLE:
-# CMAKE_QML_PLUGIN_SUFFIX_DEBUG = "_debug"
-
-#### Keys ignored in scope 8:.:.:qmlimportscanner.pro:else:
-# CMAKE_QML_PLUGIN_SUFFIX_DEBUG = <EMPTY>
-
-#### Keys ignored in scope 9:.:.:qmlimportscanner.pro:( QT_FEATURE_debug_and_release OR QT_CONFIG___contains___debug,debug OR release ):
-# CMAKE_DEBUG_TYPE = "debug"
-
-#### Keys ignored in scope 10:.:.:qmlimportscanner.pro:( QT_FEATURE_debug_and_release OR QT_CONFIG___contains___release,debug OR release ):
-# CMAKE_RELEASE_TYPE = "release"
-
-#### Keys ignored in scope 11:.:.:qmlimportscanner.pro:QT_FEATURE_debug_and_release:
-# CMAKE_DEBUG_AND_RELEASE = "TRUE"
-
-#### Keys ignored in scope 12:.:.:qmlimportscanner.pro:else:
-# CMAKE_DEBUG_AND_RELEASE = "FALSE"
diff --git a/tools/qmlimportscanner/CMakeLists.txt b/tools/qmlimportscanner/CMakeLists.txt
index 61e927b6fe..e30490532d 100644
--- a/tools/qmlimportscanner/CMakeLists.txt
+++ b/tools/qmlimportscanner/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qmlimportscanner.pro.
#####################################################################
@@ -8,15 +11,17 @@ qt_get_tool_target_name(target_name qmlimportscanner)
qt_internal_add_tool(${target_name}
TARGET_DESCRIPTION "QML Import Scanner"
TOOLS_TARGET Qml # special case
+ INSTALL_DIR "${INSTALL_LIBEXECDIR}"
SOURCES
main.cpp
DEFINES
QT_NO_CAST_FROM_ASCII
QT_NO_CAST_TO_ASCII
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::QmlCompilerPrivate
- Qt::QmlDevToolsPrivate
+ Qt::QmlPrivate
)
+qt_internal_return_unless_building_tools()
# special case begin
# Create a dummy package that will just find Qml package, for backwards compatibility reasons.
@@ -33,13 +38,18 @@ configure_package_config_file(
)
write_basic_package_version_file(
- "${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${target}ConfigVersion.cmake"
+ "${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${target}ConfigVersionImpl.cmake"
VERSION ${PROJECT_VERSION}
COMPATIBILITY AnyNewerVersion
)
+qt_internal_write_qt_package_version_file(
+ "${INSTALL_CMAKE_NAMESPACE}${target}"
+ "${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${target}ConfigVersion.cmake"
+)
qt_install(FILES
"${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${target}Config.cmake"
"${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${target}ConfigVersion.cmake"
+ "${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${target}ConfigVersionImpl.cmake"
DESTINATION "${config_install_dir}"
COMPONENT Devel
)
diff --git a/tools/qmlimportscanner/main.cpp b/tools/qmlimportscanner/main.cpp
index ea17df921f..7c8f1bc98e 100644
--- a/tools/qmlimportscanner/main.cpp
+++ b/tools/qmlimportscanner/main.cpp
@@ -1,61 +1,47 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <private/qqmljslexer_p.h>
#include <private/qqmljsparser_p.h>
#include <private/qqmljsast_p.h>
-#include <private/qv4codegen_p.h>
-#include <private/qv4staticvalue_p.h>
-#include <private/qqmlirbuilder_p.h>
#include <private/qqmljsdiagnosticmessage_p.h>
#include <private/qqmldirparser_p.h>
#include <private/qqmljsresourcefilemapper_p.h>
#include <QtCore/QCoreApplication>
+#include <QtCore/QDebug>
+#include <QtCore/QDateTime>
#include <QtCore/QDir>
#include <QtCore/QDirIterator>
#include <QtCore/QFile>
#include <QtCore/QFileInfo>
+#include <QtCore/QHash>
#include <QtCore/QSet>
#include <QtCore/QStringList>
#include <QtCore/QMetaObject>
#include <QtCore/QMetaProperty>
#include <QtCore/QVariant>
+#include <QtCore/QVariantMap>
#include <QtCore/QJsonObject>
#include <QtCore/QJsonArray>
#include <QtCore/QJsonDocument>
#include <QtCore/QLibraryInfo>
+#include <QtCore/QLoggingCategory>
#include <iostream>
#include <algorithm>
+#include <unordered_map>
+#include <unordered_set>
QT_USE_NAMESPACE
+using namespace Qt::StringLiterals;
+
+Q_LOGGING_CATEGORY(lcImportScanner, "qt.qml.import.scanner");
+Q_LOGGING_CATEGORY(lcImportScannerFiles, "qt.qml.import.scanner.files");
+
+using FileImportsWithoutDepsCache = QHash<QString, QVariantList>;
+
namespace {
QStringList g_qmlImportPaths;
@@ -65,27 +51,31 @@ inline QString versionLiteral() { return QStringLiteral("version"); }
inline QString nameLiteral() { return QStringLiteral("name"); }
inline QString relativePathLiteral() { return QStringLiteral("relativePath"); }
inline QString pluginsLiteral() { return QStringLiteral("plugins"); }
+inline QString pluginIsOptionalLiteral() { return QStringLiteral("pluginIsOptional"); }
inline QString pathLiteral() { return QStringLiteral("path"); }
inline QString classnamesLiteral() { return QStringLiteral("classnames"); }
inline QString dependenciesLiteral() { return QStringLiteral("dependencies"); }
inline QString moduleLiteral() { return QStringLiteral("module"); }
inline QString javascriptLiteral() { return QStringLiteral("javascript"); }
inline QString directoryLiteral() { return QStringLiteral("directory"); }
+inline QString linkTargetLiteral()
+{
+ return QStringLiteral("linkTarget");
+}
+inline QString componentsLiteral() { return QStringLiteral("components"); }
+inline QString scriptsLiteral() { return QStringLiteral("scripts"); }
+inline QString preferLiteral() { return QStringLiteral("prefer"); }
void printUsage(const QString &appNameIn)
{
- const std::wstring appName = appNameIn.toStdWString();
-#ifndef QT_BOOTSTRAPPED
+ const std::string appName = appNameIn.toStdString();
const QString qmlPath = QLibraryInfo::path(QLibraryInfo::QmlImportsPath);
-#else
- const QString qmlPath = QStringLiteral("/home/user/dev/qt-install/qml");
-#endif
- std::wcerr
+ std::cerr
<< "Usage: " << appName << " -rootPath path/to/app/qml/directory -importPath path/to/qt/qml/directory\n"
" " << appName << " -qmlFiles file1 file2 -importPath path/to/qt/qml/directory\n"
" " << appName << " -qrcFiles file1.qrc file2.qrc -importPath path/to/qt/qml/directory\n\n"
"Example: " << appName << " -rootPath . -importPath "
- << QDir::toNativeSeparators(qmlPath).toStdWString()
+ << QDir::toNativeSeparators(qmlPath).toStdString()
<< '\n';
}
@@ -142,8 +132,9 @@ QVariantList findImportsInAst(QQmlJS::AST::UiHeaderItemList *headerItemList, con
return imports;
}
-QVariantList findQmlImportsInQmlFile(const QString &filePath);
-QVariantList findQmlImportsInJavascriptFile(const QString &filePath);
+QVariantList findQmlImportsInFileWithoutDeps(const QString &filePath,
+ FileImportsWithoutDepsCache
+ &fileImportsWithoutDepsCache);
static QString versionSuffix(QTypeRevision version)
{
@@ -153,7 +144,18 @@ static QString versionSuffix(QTypeRevision version)
// Read the qmldir file, extract a list of plugins by
// parsing the "plugin", "import", and "classname" directives.
-QVariantMap pluginsForModulePath(const QString &modulePath, const QString &version) {
+QVariantMap pluginsForModulePath(const QString &modulePath,
+ const QString &version,
+ FileImportsWithoutDepsCache
+ &fileImportsWithoutDepsCache) {
+ using Cache = QHash<QPair<QString, QString>, QVariantMap>;
+ static Cache pluginsCache;
+ const QPair<QString, QString> cacheKey = std::make_pair(modulePath, version);
+ const Cache::const_iterator it = pluginsCache.find(cacheKey);
+ if (it != pluginsCache.end()) {
+ return *it;
+ }
+
QFile qmldirFile(modulePath + QLatin1String("/qmldir"));
if (!qmldirFile.exists()) {
qWarning() << "qmldir file not found at" << modulePath;
@@ -177,11 +179,27 @@ QVariantMap pluginsForModulePath(const QString &modulePath, const QString &versi
QVariantMap pluginInfo;
QStringList pluginNameList;
+ bool isOptional = false;
const auto plugins = parser.plugins();
- for (const auto &plugin : plugins)
+ for (const auto &plugin : plugins) {
pluginNameList.append(plugin.name);
+ isOptional = plugin.optional;
+ }
pluginInfo[pluginsLiteral()] = pluginNameList.join(QLatin1Char(' '));
+ if (plugins.size() > 1) {
+ qWarning() << QStringLiteral("Warning: \"%1\" contains multiple plugin entries. This is discouraged and does not support marking plugins as optional.").arg(modulePath);
+ isOptional = false;
+ }
+
+ if (isOptional) {
+ pluginInfo[pluginIsOptionalLiteral()] = true;
+ }
+
+ if (!parser.linkTarget().isEmpty()) {
+ pluginInfo[linkTargetLiteral()] = parser.linkTarget();
+ }
+
pluginInfo[classnamesLiteral()] = parser.classNames().join(QLatin1Char(' '));
QStringList importsAndDependencies;
@@ -203,15 +221,23 @@ QVariantMap pluginsForModulePath(const QString &modulePath, const QString &versi
}
QVariantList importsFromFiles;
+ QStringList componentFiles;
+ QStringList scriptFiles;
const auto components = parser.components();
for (const auto &component : components) {
+ const QString componentFullPath = modulePath + QLatin1Char('/') + component.fileName;
+ componentFiles.append(componentFullPath);
importsFromFiles
- += findQmlImportsInQmlFile(modulePath + QLatin1Char('/') + component.fileName);
+ += findQmlImportsInFileWithoutDeps(componentFullPath,
+ fileImportsWithoutDepsCache);
}
const auto scripts = parser.scripts();
for (const auto &script : scripts) {
+ const QString scriptFullPath = modulePath + QLatin1Char('/') + script.fileName;
+ scriptFiles.append(scriptFullPath);
importsFromFiles
- += findQmlImportsInJavascriptFile(modulePath + QLatin1Char('/') + script.fileName);
+ += findQmlImportsInFileWithoutDeps(scriptFullPath,
+ fileImportsWithoutDepsCache);
}
for (const QVariant &import : importsFromFiles) {
@@ -224,8 +250,23 @@ QVariantMap pluginsForModulePath(const QString &modulePath, const QString &versi
version.isEmpty() ? name : (name + QLatin1Char(' ') + version));
}
- if (!importsAndDependencies.isEmpty())
+ if (!importsAndDependencies.isEmpty()) {
+ importsAndDependencies.removeDuplicates();
pluginInfo[dependenciesLiteral()] = importsAndDependencies;
+ }
+ if (!componentFiles.isEmpty()) {
+ componentFiles.sort();
+ pluginInfo[componentsLiteral()] = componentFiles;
+ }
+ if (!scriptFiles.isEmpty()) {
+ scriptFiles.sort();
+ pluginInfo[scriptsLiteral()] = scriptFiles;
+ }
+
+ if (!parser.preferredPath().isEmpty())
+ pluginInfo[preferLiteral()] = parser.preferredPath();
+
+ pluginsCache.insert(cacheKey, pluginInfo);
return pluginInfo;
}
@@ -238,8 +279,9 @@ QPair<QString, QString> resolveImportPath(const QString &uri, const QString &ver
const QStringList parts = uri.split(dot, Qt::SkipEmptyParts);
QString ver = version;
+ QPair<QString, QString> candidate;
while (true) {
- for (const QString &qmlImportPath : qAsConst(g_qmlImportPaths)) {
+ for (const QString &qmlImportPath : std::as_const(g_qmlImportPaths)) {
// Search for the most specific version first, and search
// also for the version in parent modules. For example:
// - qml/QtQml/Models.2.0
@@ -252,17 +294,30 @@ QPair<QString, QString> resolveImportPath(const QString &uri, const QString &ver
if (relativePath.endsWith(slash))
relativePath.chop(1);
const QString candidatePath = QDir::cleanPath(qmlImportPath + slash + relativePath);
- if (QDir(candidatePath).exists())
- return qMakePair(candidatePath, relativePath); // import found
+ const QDir candidateDir(candidatePath);
+ if (candidateDir.exists()) {
+ const auto newCandidate = qMakePair(candidatePath, relativePath); // import found
+ if (candidateDir.exists(u"qmldir"_s)) // if it has a qmldir, we are fine
+ return newCandidate;
+ else if (candidate.first.isEmpty())
+ candidate = newCandidate;
+ // otherwise we keep looking if we can find the module again (with a qmldir this time)
+ }
} else {
- for (int index = parts.count() - 1; index >= 0; --index) {
+ for (int index = parts.size() - 1; index >= 0; --index) {
QString relativePath = parts.mid(0, index + 1).join(slash)
+ dot + ver + slash + parts.mid(index + 1).join(slash);
if (relativePath.endsWith(slash))
relativePath.chop(1);
const QString candidatePath = QDir::cleanPath(qmlImportPath + slash + relativePath);
- if (QDir(candidatePath).exists())
- return qMakePair(candidatePath, relativePath); // import found
+ const QDir candidateDir(candidatePath);
+ if (candidateDir.exists()) {
+ const auto newCandidate = qMakePair(candidatePath, relativePath); // import found
+ if (candidateDir.exists(u"qmldir"_s))
+ return newCandidate;
+ else if (candidate.first.isEmpty())
+ candidate = newCandidate;
+ }
}
}
}
@@ -278,58 +333,201 @@ QPair<QString, QString> resolveImportPath(const QString &uri, const QString &ver
ver = ver.mid(0, lastDot);
}
- return QPair<QString, QString>(); // not found
+ return candidate;
}
-// Find absolute file system paths and plugins for a list of modules.
-QVariantList findPathsForModuleImports(const QVariantList &imports)
+// Provides a hasher for module details stored in a QVariantMap disguised as a QVariant..
+// Only supports a subset of types.
+struct ImportVariantHasher {
+ std::size_t operator()(const QVariant &importVariant) const
+ {
+ size_t computedHash = 0;
+ QVariantMap importMap = qvariant_cast<QVariantMap>(importVariant);
+ for (auto it = importMap.constKeyValueBegin(); it != importMap.constKeyValueEnd(); ++it) {
+ const QString &key = it->first;
+ const QVariant &value = it->second;
+
+ if (!value.isValid() || value.isNull()) {
+ computedHash = qHashMulti(computedHash, key, 0);
+ continue;
+ }
+
+ const auto valueTypeId = value.typeId();
+ switch (valueTypeId) {
+ case QMetaType::QString:
+ computedHash = qHashMulti(computedHash, key, value.toString());
+ break;
+ case QMetaType::Bool:
+ computedHash = qHashMulti(computedHash, key, value.toBool());
+ break;
+ case QMetaType::QStringList:
+ computedHash = qHashMulti(computedHash, key, value.toStringList());
+ break;
+ default:
+ Q_ASSERT_X(valueTypeId, "ImportVariantHasher", "Invalid variant type detected");
+ break;
+ }
+ }
+
+ return computedHash;
+ }
+};
+
+using ImportDetailsAndDeps = QPair<QVariantMap, QStringList>;
+
+// Returns the import information as it will be written out to the json / .cmake file.
+// The dependencies are not stored in the same QVariantMap because we don't currently need that
+// information in the output file.
+ImportDetailsAndDeps
+getImportDetails(const QVariant &inputImport,
+ FileImportsWithoutDepsCache &fileImportsWithoutDepsCache) {
+
+ using Cache = std::unordered_map<QVariant, ImportDetailsAndDeps, ImportVariantHasher>;
+ static Cache cache;
+
+ const Cache::const_iterator it = cache.find(inputImport);
+ if (it != cache.end()) {
+ return it->second;
+ }
+
+ QVariantMap import = qvariant_cast<QVariantMap>(inputImport);
+ QStringList dependencies;
+ if (import.value(typeLiteral()) == moduleLiteral()) {
+ const QString version = import.value(versionLiteral()).toString();
+ const QPair<QString, QString> paths =
+ resolveImportPath(import.value(nameLiteral()).toString(), version);
+ QVariantMap plugininfo;
+ if (!paths.first.isEmpty()) {
+ import.insert(pathLiteral(), paths.first);
+ import.insert(relativePathLiteral(), paths.second);
+ plugininfo = pluginsForModulePath(paths.first,
+ version,
+ fileImportsWithoutDepsCache);
+ }
+ QString linkTarget = plugininfo.value(linkTargetLiteral()).toString();
+ QString plugins = plugininfo.value(pluginsLiteral()).toString();
+ bool isOptional = plugininfo.value(pluginIsOptionalLiteral(), QVariant(false)).toBool();
+ QString classnames = plugininfo.value(classnamesLiteral()).toString();
+ QStringList components = plugininfo.value(componentsLiteral()).toStringList();
+ QStringList scripts = plugininfo.value(scriptsLiteral()).toStringList();
+ QString prefer = plugininfo.value(preferLiteral()).toString();
+ if (!linkTarget.isEmpty())
+ import.insert(linkTargetLiteral(), linkTarget);
+ if (!plugins.isEmpty())
+ import.insert(QStringLiteral("plugin"), plugins);
+ if (isOptional)
+ import.insert(pluginIsOptionalLiteral(), true);
+ if (!classnames.isEmpty())
+ import.insert(QStringLiteral("classname"), classnames);
+ if (plugininfo.contains(dependenciesLiteral())) {
+ dependencies = plugininfo.value(dependenciesLiteral()).toStringList();
+ }
+ if (!components.isEmpty()) {
+ components.removeDuplicates();
+ import.insert(componentsLiteral(), components);
+ }
+ if (!scripts.isEmpty()) {
+ scripts.removeDuplicates();
+ import.insert(scriptsLiteral(), scripts);
+ }
+ if (!prefer.isEmpty()) {
+ import.insert(preferLiteral(), prefer);
+ }
+ }
+ import.remove(versionLiteral());
+
+ const ImportDetailsAndDeps result = {import, dependencies};
+ cache.insert({inputImport, result});
+ return result;
+}
+
+// Parse a dependency string line into a QVariantMap, to be used as a key when processing imports
+// in getGetDetailedModuleImportsIncludingDependencies.
+QVariantMap dependencyStringToImport(const QString &line) {
+ const auto dep = QStringView{line}.split(QLatin1Char(' '), Qt::SkipEmptyParts);
+ const QString name = dep[0].toString();
+ QVariantMap depImport;
+ depImport[typeLiteral()] = moduleLiteral();
+ depImport[nameLiteral()] = name;
+ if (dep.size() > 1)
+ depImport[versionLiteral()] = dep[1].toString();
+ return depImport;
+}
+
+// Returns details of given input import and its recursive module dependencies.
+// The details include absolute file system paths for the the module plugin, components,
+// etc.
+// An internal cache is used to prevent repeated computation for the same input module.
+QVariantList getGetDetailedModuleImportsIncludingDependencies(
+ const QVariant &inputImport,
+ FileImportsWithoutDepsCache &fileImportsWithoutDepsCache)
{
+ using Cache = std::unordered_map<QVariant, QVariantList, ImportVariantHasher>;
+ static Cache importsCacheWithDeps;
+
+ const Cache::const_iterator it = importsCacheWithDeps.find(inputImport);
+ if (it != importsCacheWithDeps.end()) {
+ return it->second;
+ }
+
QVariantList done;
- QVariantList importsCopy(imports);
-
- for (int i = 0; i < importsCopy.length(); ++i) {
- QVariantMap import = qvariant_cast<QVariantMap>(importsCopy.at(i));
- if (import.value(typeLiteral()) == moduleLiteral()) {
- const QString version = import.value(versionLiteral()).toString();
- const QPair<QString, QString> paths =
- resolveImportPath(import.value(nameLiteral()).toString(), version);
- QVariantMap plugininfo;
- if (!paths.first.isEmpty()) {
- import.insert(pathLiteral(), paths.first);
- import.insert(relativePathLiteral(), paths.second);
- plugininfo = pluginsForModulePath(paths.first, version);
- }
- QString plugins = plugininfo.value(pluginsLiteral()).toString();
- QString classnames = plugininfo.value(classnamesLiteral()).toString();
- if (!plugins.isEmpty())
- import.insert(QStringLiteral("plugin"), plugins);
- if (!classnames.isEmpty())
- import.insert(QStringLiteral("classname"), classnames);
- if (plugininfo.contains(dependenciesLiteral())) {
- const QStringList dependencies = plugininfo.value(dependenciesLiteral()).toStringList();
- for (const QString &line : dependencies) {
- const auto dep = QStringView{line}.split(QLatin1Char(' '), Qt::SkipEmptyParts);
- const QString name = dep[0].toString();
- QVariantMap depImport;
- depImport[typeLiteral()] = moduleLiteral();
- depImport[nameLiteral()] = name;
- if (dep.length() > 1)
- depImport[versionLiteral()] = dep[1].toString();
-
- if (!importsCopy.contains(depImport))
- importsCopy.append(depImport);
+ QVariantList importsToProcess;
+ std::unordered_set<QVariant, ImportVariantHasher> importsSeen;
+ importsToProcess.append(inputImport);
+
+ for (int i = 0; i < importsToProcess.size(); ++i) {
+ const QVariant importToProcess = importsToProcess.at(i);
+ auto [details, deps] = getImportDetails(importToProcess, fileImportsWithoutDepsCache);
+ if (details.value(typeLiteral()) == moduleLiteral()) {
+ for (const QString &line : deps) {
+ const QVariantMap depImport = dependencyStringToImport(line);
+
+ // Skip self-dependencies.
+ if (depImport == importToProcess)
+ continue;
+
+ if (importsSeen.find(depImport) == importsSeen.end()) {
+ importsToProcess.append(depImport);
+ importsSeen.insert(depImport);
}
}
}
- import.remove(versionLiteral());
- done.append(import);
+ done.append(details);
}
+
+ importsCacheWithDeps.insert({inputImport, done});
return done;
}
+QVariantList mergeImports(const QVariantList &a, const QVariantList &b);
+
+// Returns details of given input imports and their recursive module dependencies.
+QVariantList getGetDetailedModuleImportsIncludingDependencies(
+ const QVariantList &inputImports,
+ FileImportsWithoutDepsCache &fileImportsWithoutDepsCache)
+{
+ QVariantList result;
+
+ // Get rid of duplicates in input module list.
+ QVariantList inputImportsCopy;
+ inputImportsCopy = mergeImports(inputImportsCopy, inputImports);
+
+ // Collect recursive dependencies for each input module and merge into result, discarding
+ // duplicates.
+ for (auto it = inputImportsCopy.begin(); it != inputImportsCopy.end(); ++it) {
+ QVariantList imports = getGetDetailedModuleImportsIncludingDependencies(
+ *it, fileImportsWithoutDepsCache);
+ result = mergeImports(result, imports);
+ }
+ return result;
+}
+
// Scan a single qml file for import statements
QVariantList findQmlImportsInQmlCode(const QString &filePath, const QString &code)
{
+ qCDebug(lcImportScannerFiles) << "Parsing code and finding imports in" << filePath
+ << "TS:" << QDateTime::currentMSecsSinceEpoch();
+
QQmlJS::Engine engine;
QQmlJS::Lexer lexer(&engine);
lexer.setCode(code, /*line = */ 1);
@@ -425,9 +623,17 @@ QVariantList findQmlImportsInJavascriptFile(const QString &filePath)
return collector.imports;
}
-// Scan a single qml or js file for import statements
-QVariantList findQmlImportsInFile(const QString &filePath)
+// Scan a single qml or js file for import statements without resolving dependencies.
+QVariantList findQmlImportsInFileWithoutDeps(const QString &filePath,
+ FileImportsWithoutDepsCache
+ &fileImportsWithoutDepsCache)
{
+ const FileImportsWithoutDepsCache::const_iterator it =
+ fileImportsWithoutDepsCache.find(filePath);
+ if (it != fileImportsWithoutDepsCache.end()) {
+ return *it;
+ }
+
QVariantList imports;
if (filePath == QLatin1String("-")) {
QFile f;
@@ -437,12 +643,47 @@ QVariantList findQmlImportsInFile(const QString &filePath)
imports = findQmlImportsInQmlFile(filePath);
} else if (filePath.endsWith(QLatin1String(".js"))) {
imports = findQmlImportsInJavascriptFile(filePath);
+ } else {
+ qCDebug(lcImportScanner) << "Skipping file because it's not a .qml/.js file";
+ return imports;
}
- return findPathsForModuleImports(imports);
+ fileImportsWithoutDepsCache.insert(filePath, imports);
+ return imports;
+}
+
+// Scan a single qml or js file for import statements, resolve dependencies and return the full
+// list of modules the file depends on.
+QVariantList findQmlImportsInFile(const QString &filePath,
+ FileImportsWithoutDepsCache
+ &fileImportsWithoutDepsCache) {
+ const auto fileProcessTimeBegin = QDateTime::currentDateTime();
+
+ QVariantList imports = findQmlImportsInFileWithoutDeps(filePath,
+ fileImportsWithoutDepsCache);
+ if (imports.empty())
+ return imports;
+
+ const auto pathsTimeBegin = QDateTime::currentDateTime();
+
+ qCDebug(lcImportScanner) << "Finding module paths for imported modules in" << filePath
+ << "TS:" << pathsTimeBegin.toMSecsSinceEpoch();
+ QVariantList importPaths = getGetDetailedModuleImportsIncludingDependencies(
+ imports, fileImportsWithoutDepsCache);
+
+ const auto pathsTimeEnd = QDateTime::currentDateTime();
+ const auto duration = pathsTimeBegin.msecsTo(pathsTimeEnd);
+ const auto fileProcessingDuration = fileProcessTimeBegin.msecsTo(pathsTimeEnd);
+ qCDebug(lcImportScanner) << "Found module paths:" << importPaths.size()
+ << "TS:" << pathsTimeEnd.toMSecsSinceEpoch()
+ << "Path resolution duration:" << duration << "msecs";
+ qCDebug(lcImportScanner) << "Scan duration:" << fileProcessingDuration << "msecs";
+ return importPaths;
}
// Merge two lists of imports, discard duplicates.
+// Empirical tests show that for a small amount of values, the n^2 QVariantList comparison
+// is still faster than using an unordered_set + hashing a complex QVariantMap.
QVariantList mergeImports(const QVariantList &a, const QVariantList &b)
{
QVariantList merged = a;
@@ -472,7 +713,9 @@ struct pathStartsWith {
// Scan all qml files in directory for import statements
-QVariantList findQmlImportsInDirectory(const QString &qmlDir)
+QVariantList findQmlImportsInDirectory(const QString &qmlDir,
+ FileImportsWithoutDepsCache
+ &fileImportsWithoutDepsCache)
{
QVariantList ret;
if (qmlDir.isEmpty())
@@ -506,8 +749,15 @@ QVariantList findQmlImportsInDirectory(const QString &qmlDir)
}
for (const QFileInfo &x : entries)
- if (x.isFile())
- ret = mergeImports(ret, findQmlImportsInFile(x.absoluteFilePath()));
+ if (x.isFile()) {
+ const auto entryAbsolutePath = x.absoluteFilePath();
+ qCDebug(lcImportScanner) << "Scanning file" << entryAbsolutePath
+ << "TS:" << QDateTime::currentMSecsSinceEpoch();
+ ret = mergeImports(ret,
+ findQmlImportsInFile(
+ entryAbsolutePath,
+ fileImportsWithoutDepsCache));
+ }
}
return ret;
}
@@ -515,19 +765,29 @@ QVariantList findQmlImportsInDirectory(const QString &qmlDir)
// Find qml imports recursively from a root set of qml files.
// The directories in qmlDirs are searched recursively.
// The files in qmlFiles parsed directly.
-QVariantList findQmlImportsRecursively(const QStringList &qmlDirs, const QStringList &scanFiles)
+QVariantList findQmlImportsRecursively(const QStringList &qmlDirs,
+ const QStringList &scanFiles,
+ FileImportsWithoutDepsCache
+ &fileImportsWithoutDepsCache)
{
QVariantList ret;
+ qCDebug(lcImportScanner) << "Scanning" << qmlDirs.size() << "root directories and"
+ << scanFiles.size() << "files.";
+
// Scan all app root qml directories for imports
for (const QString &qmlDir : qmlDirs) {
- QVariantList imports = findQmlImportsInDirectory(qmlDir);
+ qCDebug(lcImportScanner) << "Scanning root" << qmlDir
+ << "TS:" << QDateTime::currentMSecsSinceEpoch();
+ QVariantList imports = findQmlImportsInDirectory(qmlDir, fileImportsWithoutDepsCache);
ret = mergeImports(ret, imports);
}
// Scan app qml files for imports
for (const QString &file : scanFiles) {
- QVariantList imports = findQmlImportsInFile(file);
+ qCDebug(lcImportScanner) << "Scanning file" << file
+ << "TS:" << QDateTime::currentMSecsSinceEpoch();
+ QVariantList imports = findQmlImportsInFile(file, fileImportsWithoutDepsCache);
ret = mergeImports(ret, imports);
}
@@ -550,8 +810,20 @@ QString generateCmakeIncludeFileContent(const QVariantList &importList) {
const QMap<QString, QVariant> &importDict = importVariant.toMap();
for (auto it = importDict.cbegin(); it != importDict.cend(); ++it) {
- s << it.key().toUpper() << QLatin1Char(';')
- << it.value().toString() << QLatin1Char(';');
+ s << it.key().toUpper() << QLatin1Char(';');
+ // QVariant can implicitly convert QString to the QStringList with the single
+ // element, let's use this.
+ QStringList args = it.value().toStringList();
+ if (args.isEmpty()) {
+ // This should not happen, but if it does, the result of the
+ // 'cmake_parse_arguments' call will be incorrect, so follow up semicolon
+ // indicates that the single-/multiarg option is empty.
+ s << QLatin1Char(';');
+ } else {
+ for (auto arg : args) {
+ s << arg << QLatin1Char(';');
+ }
+ }
}
s << QStringLiteral("\")\n");
++importsCount;
@@ -564,59 +836,102 @@ QString generateCmakeIncludeFileContent(const QVariantList &importList) {
return content;
}
+bool argumentsFromCommandLineAndFile(QStringList &allArguments, const QStringList &arguments)
+{
+ allArguments.reserve(arguments.size());
+ for (const QString &argument : arguments) {
+ // "@file" doesn't start with a '-' so we can't use QCommandLineParser for it
+ if (argument.startsWith(QLatin1Char('@'))) {
+ QString optionsFile = argument;
+ optionsFile.remove(0, 1);
+ if (optionsFile.isEmpty()) {
+ fprintf(stderr, "The @ option requires an input file");
+ return false;
+ }
+ QFile f(optionsFile);
+ if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ fprintf(stderr, "Cannot open options file specified with @");
+ return false;
+ }
+ while (!f.atEnd()) {
+ QString line = QString::fromLocal8Bit(f.readLine().trimmed());
+ if (!line.isEmpty())
+ allArguments << line;
+ }
+ } else {
+ allArguments << argument;
+ }
+ }
+ return true;
+}
+
} // namespace
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
QCoreApplication::setApplicationVersion(QLatin1String(QT_VERSION_STR));
- QStringList args = app.arguments();
+ QStringList args;
+ if (!argumentsFromCommandLineAndFile(args, app.arguments()))
+ return EXIT_FAILURE;
const QString appName = QFileInfo(app.applicationFilePath()).baseName();
if (args.size() < 2) {
printUsage(appName);
return 1;
}
+ // QQmlDirParser returnes QMultiHashes. Ensure deterministic output.
+ QHashSeed::setDeterministicGlobalSeed();
+
QStringList qmlRootPaths;
QStringList scanFiles;
QStringList qmlImportPaths;
QStringList qrcFiles;
bool generateCmakeContent = false;
+ QString outputFile;
int i = 1;
- while (i < args.count()) {
+ while (i < args.size()) {
const QString &arg = args.at(i);
++i;
QStringList *argReceiver = nullptr;
if (!arg.startsWith(QLatin1Char('-')) || arg == QLatin1String("-")) {
qmlRootPaths += arg;
} else if (arg == QLatin1String("-rootPath")) {
- if (i >= args.count())
+ if (i >= args.size())
std::cerr << "-rootPath requires an argument\n";
argReceiver = &qmlRootPaths;
} else if (arg == QLatin1String("-qmlFiles")) {
- if (i >= args.count())
+ if (i >= args.size())
std::cerr << "-qmlFiles requires an argument\n";
argReceiver = &scanFiles;
} else if (arg == QLatin1String("-jsFiles")) {
- if (i >= args.count())
+ if (i >= args.size())
std::cerr << "-jsFiles requires an argument\n";
argReceiver = &scanFiles;
} else if (arg == QLatin1String("-importPath")) {
- if (i >= args.count())
+ if (i >= args.size())
std::cerr << "-importPath requires an argument\n";
argReceiver = &qmlImportPaths;
} else if (arg == QLatin1String("-cmake-output")) {
generateCmakeContent = true;
} else if (arg == QLatin1String("-qrcFiles")) {
argReceiver = &qrcFiles;
+ } else if (arg == QLatin1String("-output-file")) {
+ if (i >= args.size()) {
+ std::cerr << "-output-file requires an argument\n";
+ return 1;
+ }
+ outputFile = args.at(i);
+ ++i;
+ continue;
} else {
std::cerr << qPrintable(appName) << ": Invalid argument: \""
<< qPrintable(arg) << "\"\n";
return 1;
}
- while (i < args.count()) {
+ while (i < args.size()) {
const QString arg = args.at(i);
if (arg.startsWith(QLatin1Char('-')) && arg != QLatin1String("-"))
break;
@@ -625,8 +940,12 @@ int main(int argc, char *argv[])
std::cerr << qPrintable(appName) << ": No such file or directory: \""
<< qPrintable(arg) << "\"\n";
return 1;
- } else {
+ } else if (argReceiver) {
*argReceiver += arg;
+ } else {
+ std::cerr << qPrintable(appName) << ": Invalid argument: \""
+ << qPrintable(arg) << "\"\n";
+ return 1;
}
}
}
@@ -638,8 +957,13 @@ int main(int argc, char *argv[])
g_qmlImportPaths = qmlImportPaths;
+ FileImportsWithoutDepsCache fileImportsWithoutDepsCache;
+
// Find the imports!
- QVariantList imports = findQmlImportsRecursively(qmlRootPaths, scanFiles);
+ QVariantList imports = findQmlImportsRecursively(qmlRootPaths,
+ scanFiles,
+ fileImportsWithoutDepsCache
+ );
QByteArray content;
if (generateCmakeContent) {
@@ -650,6 +974,17 @@ int main(int argc, char *argv[])
content = QJsonDocument(QJsonArray::fromVariantList(imports)).toJson();
}
- std::cout << content.constData() << std::endl;
+ if (outputFile.isEmpty()) {
+ std::cout << content.constData() << std::endl;
+ } else {
+ QFile f(outputFile);
+ if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ std::cerr << qPrintable(appName) << ": Unable to write to output file: \""
+ << qPrintable(outputFile) << "\"\n";
+ return 1;
+ }
+ QTextStream out(&f);
+ out << content << "\n";
+ }
return 0;
}
diff --git a/tools/qmljs/.prev_CMakeLists.txt b/tools/qmljs/.prev_CMakeLists.txt
deleted file mode 100644
index 86d0a31552..0000000000
--- a/tools/qmljs/.prev_CMakeLists.txt
+++ /dev/null
@@ -1,89 +0,0 @@
-# Generated from qmljs.pro.
-
-#####################################################################
-## qmljs Tool:
-#####################################################################
-
-qt_get_tool_target_name(target_name qmljs)
-qt_internal_add_tool(${target_name}
- TARGET_DESCRIPTION "QML JavaScript Tool"
- SOURCES
- qmljs.cpp
- DEFINES
- BUILDING_QT__
- ENABLE_ASSEMBLER_WX_EXCLUSIVE=1
- ENABLE_DFG_JIT=0
- ENABLE_DFG_JIT_UTILITY_METHODS=1
- ENABLE_JIT_CONSTANT_BLINDING=0
- ENABLE_LLINT=0
- JS_EXPORT_PRIVATE=""
- WTFInvokeCrashHook=qmlWTFInvokeCrashHook
- WTFReportAssertionFailure=qmlWTFReportAssertionFailure
- WTFReportAssertionFailureWithMessage=qmlWTFReportAssertionFailureWithMessage
- WTFReportBacktrace=qmlWTFReportBacktrace
- WTF_EXPORT_PRIVATE=""
- INCLUDE_DIRECTORIES
- ../../src/3rdparty/masm
- ../../src/3rdparty/masm/assembler
- ../../src/3rdparty/masm/disassembler
- ../../src/3rdparty/masm/disassembler/udis86
- ../../src/3rdparty/masm/jit
- ../../src/3rdparty/masm/runtime
- ../../src/3rdparty/masm/stubs
- ../../src/3rdparty/masm/stubs/runtime
- ../../src/3rdparty/masm/stubs/wtf
- ../../src/3rdparty/masm/wtf
- PUBLIC_LIBRARIES
- Qt::CorePrivate
- Qt::QmlPrivate
-)
-
-#### Keys ignored in scope 1:.:.:qmljs.pro:<TRUE>:
-# QMAKE_TARGET_DESCRIPTION = "QML" "JavaScript" "Tool"
-# TEMPLATE = "app"
-
-## Scopes:
-#####################################################################
-
-qt_internal_extend_target(${target_name} CONDITION WIN32
- DEFINES
- NOMINMAX
-)
-
-qt_internal_extend_target(${target_name} CONDITION disassembler AND ((TEST_architecture_arch STREQUAL "i386") OR (TEST_architecture_arch STREQUAL "x86_64"))
- DEFINES
- WTF_USE_UDIS86=1
-)
-
-qt_internal_extend_target(${target_name} CONDITION (TEST_architecture_arch STREQUAL "arm") AND disassembler
- DEFINES
- WTF_USE_ARMV7_DISASSEMBLER=1
-)
-
-qt_internal_extend_target(${target_name} CONDITION (TEST_architecture_arch STREQUAL "arm64") AND disassembler
- DEFINES
- WTF_USE_ARM64_DISASSEMBLER=1
-)
-
-qt_internal_extend_target(${target_name} CONDITION (TEST_architecture_arch STREQUAL "mips") AND disassembler
- DEFINES
- WTF_USE_MIPS32_DISASSEMBLER=1
-)
-
-qt_internal_extend_target(${target_name} CONDITION NOT disassembler
- DEFINES
- WTF_USE_UDIS86=0
-)
-
-qt_internal_extend_target(${target_name} CONDITION CMAKE_BUILD_TYPE STREQUAL Release
- DEFINES
- NDEBUG
-)
-
-qt_internal_extend_target(${target_name} CONDITION GCC AND QT_COMPILER_VERSION_MAJOR STRGREATER 6 AND NOT CLANG AND NOT ICC
- COMPILE_OPTIONS
- -Wno-expansion-to-defined
-)
-
-#### Keys ignored in scope 12:.:../../src/3rdparty/masm:../../src/3rdparty/masm/masm-defs.pri:(QT_COMPILER_VERSION_MAJOR STRGREATER 6):
-# QMAKE_CXXFLAGS_WARN_ON = "-Wno-expansion-to-defined"
diff --git a/tools/qmljs/CMakeLists.txt b/tools/qmljs/CMakeLists.txt
index 147ddf008c..40faa5d013 100644
--- a/tools/qmljs/CMakeLists.txt
+++ b/tools/qmljs/CMakeLists.txt
@@ -1,13 +1,14 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qmljs.pro.
#####################################################################
-## qmljs Tool:
+## qmljs App:
#####################################################################
-qt_get_tool_target_name(target_name qmljs)
-qt_internal_add_tool(${target_name}
+qt_internal_add_app(qmljs
TARGET_DESCRIPTION "QML JavaScript Tool"
- TOOLS_TARGET Qml # special case
SOURCES
qmljs.cpp
DEFINES
@@ -34,11 +35,13 @@ qt_internal_add_tool(${target_name}
../../src/3rdparty/masm/stubs/runtime
../../src/3rdparty/masm/stubs/wtf
../../src/3rdparty/masm/wtf
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::CorePrivate
Qt::QmlPrivate
)
+set_target_properties(qmljs PROPERTIES WIN32_EXECUTABLE FALSE)
+
#### Keys ignored in scope 1:.:.:qmljs.pro:<TRUE>:
# QMAKE_TARGET_DESCRIPTION = "QML" "JavaScript" "Tool"
# TEMPLATE = "app"
@@ -46,42 +49,37 @@ qt_internal_add_tool(${target_name}
## Scopes:
#####################################################################
-qt_internal_extend_target(${target_name} CONDITION WIN32
- DEFINES
- NOMINMAX
-)
-
-qt_internal_extend_target(${target_name} CONDITION disassembler AND ((TEST_architecture_arch STREQUAL "i386") OR (TEST_architecture_arch STREQUAL "x86_64"))
+qt_internal_extend_target(qmljs CONDITION disassembler AND ((TEST_architecture_arch STREQUAL "i386") OR (TEST_architecture_arch STREQUAL "x86_64"))
DEFINES
WTF_USE_UDIS86=1
)
-qt_internal_extend_target(${target_name} CONDITION (TEST_architecture_arch STREQUAL "arm") AND disassembler
+qt_internal_extend_target(qmljs CONDITION (TEST_architecture_arch STREQUAL "arm") AND disassembler
DEFINES
WTF_USE_ARMV7_DISASSEMBLER=1
)
-qt_internal_extend_target(${target_name} CONDITION (TEST_architecture_arch STREQUAL "arm64") AND disassembler
+qt_internal_extend_target(qmljs CONDITION (TEST_architecture_arch STREQUAL "arm64") AND disassembler
DEFINES
WTF_USE_ARM64_DISASSEMBLER=1
)
-qt_internal_extend_target(${target_name} CONDITION (TEST_architecture_arch STREQUAL "mips") AND disassembler
+qt_internal_extend_target(qmljs CONDITION (TEST_architecture_arch STREQUAL "mips") AND disassembler
DEFINES
WTF_USE_MIPS32_DISASSEMBLER=1
)
-qt_internal_extend_target(${target_name} CONDITION NOT disassembler
+qt_internal_extend_target(qmljs CONDITION NOT disassembler
DEFINES
WTF_USE_UDIS86=0
)
-qt_internal_extend_target(${target_name} CONDITION CMAKE_BUILD_TYPE STREQUAL Release
+qt_internal_extend_target(qmljs CONDITION CMAKE_BUILD_TYPE STREQUAL Release
DEFINES
NDEBUG
)
-qt_internal_extend_target(${target_name} CONDITION GCC AND QT_COMPILER_VERSION_MAJOR STRGREATER 6 AND NOT CLANG AND NOT ICC
+qt_internal_extend_target(qmljs CONDITION GCC AND QT_COMPILER_VERSION_MAJOR STRGREATER 6 AND NOT CLANG AND NOT ICC
COMPILE_OPTIONS
-Wno-expansion-to-defined
)
diff --git a/tools/qmljs/qmljs.cpp b/tools/qmljs/qmljs.cpp
index ba5e5f553c..8a1c4d0ded 100644
--- a/tools/qmljs/qmljs.cpp
+++ b/tools/qmljs/qmljs.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the V4VM module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "private/qv4object_p.h"
#include "private/qv4runtime_p.h"
@@ -44,6 +19,7 @@
#include <QtCore/QFile>
#include <QtCore/QFileInfo>
#include <QtCore/QDateTime>
+#include <QtCore/qcommandlineparser.h>
#include <private/qqmljsengine_p.h>
#include <private/qqmljslexer_p.h>
#include <private/qqmljsparser_p.h>
@@ -74,43 +50,48 @@ int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
QCoreApplication::setApplicationVersion(QLatin1String(QT_VERSION_STR));
- QStringList args = app.arguments();
- args.removeFirst();
- bool runAsQml = false;
- bool runAsModule = false;
- bool cache = false;
+ QCommandLineParser parser;
+ parser.addHelpOption();
+ parser.setApplicationDescription("Utility to execute scripts in QML's V4 engine");
+ parser.addVersionOption();
+ parser.addPositionalArgument("files", "Files to execute.", "[files...]");
- if (!args.isEmpty()) {
- if (args.constFirst() == QLatin1String("--jit")) {
- qputenv("QV4_JIT_CALL_THRESHOLD", QByteArray("0"));
- args.removeFirst();
- }
- if (args.constFirst() == QLatin1String("--interpret")) {
- qputenv("QV4_FORCE_INTERPRETER", QByteArray("1"));
- args.removeFirst();
- }
- if (args.constFirst() == QLatin1String("--qml")) {
- runAsQml = true;
- args.removeFirst();
- }
+ QCommandLineOption forceJit("jit", "Force JIT.");
+ parser.addOption(forceJit);
- if (args.constFirst() == QLatin1String("--module")) {
- runAsModule = true;
- args.removeFirst();
- }
+ QCommandLineOption forceInterpreter("interpret", "Force interpreter.");
+ parser.addOption(forceInterpreter);
- if (args.constFirst() == QLatin1String("--cache")) {
- cache = true;
- args.removeFirst();
- }
+ QCommandLineOption qml("qml", "Run as QML.");
+ parser.addOption(qml);
+
+ QCommandLineOption module("module", "Run as Module.");
+ parser.addOption(module);
- if (args.constFirst() == QLatin1String("--help")) {
- std::cerr << "Usage: qmljs [|--jit|--interpret|--qml] file..." << std::endl;
- return EXIT_SUCCESS;
+ QCommandLineOption cache("cache", "Use cache.");
+ parser.addOption(cache);
+
+ parser.process(app);
+
+ bool jitEnabled = false;
+
+ if (parser.isSet(forceJit)) {
+ qputenv("QV4_JIT_CALL_THRESHOLD", QByteArray("0"));
+ jitEnabled = true;
+ }
+ if (parser.isSet(forceInterpreter)) {
+ qputenv("QV4_FORCE_INTERPRETER", QByteArray("1"));
+ if (jitEnabled) {
+ std::cerr << "You cannot use 'Force JIT' and 'Force Interpreter' at the same time.";
+ return EXIT_FAILURE;
}
}
+ const bool runAsQml = parser.isSet(qml);
+ const bool runAsModule = parser.isSet(module);
+ const bool useCache = parser.isSet(cache);
+ const QStringList args = parser.positionalArguments();
QV4::ExecutionEngine vm;
@@ -119,13 +100,15 @@ int main(int argc, char *argv[])
QV4::GlobalExtensions::init(vm.globalObject, QJSEngine::ConsoleExtension | QJSEngine::GarbageCollectionExtension);
- for (const QString &fn : qAsConst(args)) {
+ for (const QString &fn : args) {
QV4::ScopedValue result(scope);
if (runAsModule) {
- auto moduleUnit = vm.loadModule(QUrl::fromLocalFile(QFileInfo(fn).absoluteFilePath()));
- if (moduleUnit) {
- if (moduleUnit->instantiate(&vm))
- moduleUnit->evaluate();
+ auto module = vm.loadModule(QUrl::fromLocalFile(QFileInfo(fn).absoluteFilePath()));
+ if (module.compiled) {
+ if (module.compiled->instantiate())
+ module.compiled->evaluate();
+ } else if (module.native) {
+ // Nothing to do. Native modules have no global code.
} else {
vm.throwError(QStringLiteral("Could not load module file"));
}
@@ -136,33 +119,34 @@ int main(int argc, char *argv[])
return EXIT_FAILURE;
}
QScopedPointer<QV4::Script> script;
- if (cache && QFile::exists(fn + QLatin1Char('c'))) {
- QQmlRefPointer<QV4::ExecutableCompilationUnit> unit
- = QV4::ExecutableCompilationUnit::create();
+ if (useCache && QFile::exists(fn + QLatin1Char('c'))) {
+ auto unit = QQml::makeRefPointer<QV4::CompiledData::CompilationUnit>();
QString error;
if (unit->loadFromDisk(QUrl::fromLocalFile(fn), QFileInfo(fn).lastModified(), &error)) {
- script.reset(new QV4::Script(&vm, nullptr, unit));
+ script.reset(new QV4::Script(
+ &vm, nullptr, vm.insertCompilationUnit(std::move(unit))));
} else {
std::cout << "Error loading" << qPrintable(fn) << "from disk cache:" << qPrintable(error) << std::endl;
}
}
if (!script) {
QByteArray ba = file.readAll();
- const QString code = QString::fromUtf8(ba.constData(), ba.length());
+ const QString code = QString::fromUtf8(ba.constData(), ba.size());
file.close();
script.reset(new QV4::Script(ctx, QV4::Compiler::ContextType::Global, code, fn));
script->parseAsBinding = runAsQml;
script->parse();
}
- if (!scope.engine->hasException) {
+ if (!scope.hasException()) {
const auto unit = script->compilationUnit;
- if (cache && unit && !(unit->unitData()->flags & QV4::CompiledData::Unit::StaticData)) {
+ if (useCache && unit && !(unit->unitData()->flags & QV4::CompiledData::Unit::StaticData)) {
if (unit->unitData()->sourceTimeStamp == 0) {
const_cast<QV4::CompiledData::Unit*>(unit->unitData())->sourceTimeStamp = QFileInfo(fn).lastModified().toMSecsSinceEpoch();
}
QString saveError;
- if (!unit->saveToDisk(QUrl::fromLocalFile(fn), &saveError)) {
+ if (!unit->baseCompilationUnit()->saveToDisk(
+ QUrl::fromLocalFile(fn), &saveError)) {
std::cout << "Error saving JS cache file: " << qPrintable(saveError) << std::endl;
}
}
@@ -171,7 +155,7 @@ int main(int argc, char *argv[])
// std::cout << t.elapsed() << " ms. elapsed" << std::endl;
}
}
- if (scope.engine->hasException) {
+ if (scope.hasException()) {
QV4::StackTrace trace;
QV4::ScopedValue ex(scope, scope.engine->catchException(&trace));
showException(ctx, ex, trace);
diff --git a/tools/qmljsrootgen/CMakeLists.txt b/tools/qmljsrootgen/CMakeLists.txt
new file mode 100644
index 0000000000..c81494b664
--- /dev/null
+++ b/tools/qmljsrootgen/CMakeLists.txt
@@ -0,0 +1,15 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_get_tool_target_name(target_name qmljsrootgen)
+qt_internal_add_tool(${target_name}
+ TARGET_DESCRIPTION "QML Global Object Metatypes Generator"
+ TOOLS_TARGET Qml # special case
+ INSTALL_DIR "${INSTALL_LIBEXECDIR}"
+ SOURCES
+ main.cpp
+ LIBRARIES
+ Qt::CorePrivate
+ Qt::QmlPrivate
+)
+qt_internal_return_unless_building_tools()
diff --git a/tools/qmljsrootgen/main.cpp b/tools/qmljsrootgen/main.cpp
new file mode 100644
index 0000000000..acd375144e
--- /dev/null
+++ b/tools/qmljsrootgen/main.cpp
@@ -0,0 +1,407 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <QtQml/private/qjsvalue_p.h>
+#include <QtQml/private/qv4propertykey_p.h>
+#include <QtQml/private/qv4global_p.h>
+#include <QtQml/private/qv4functionobject_p.h>
+#include <QtQml/qjsengine.h>
+#include <QtQml/qjsmanagedvalue.h>
+
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qcommandlineparser.h>
+
+#include <QtCore/qjsondocument.h>
+#include <QtCore/qjsonarray.h>
+#include <QtCore/qjsonobject.h>
+
+struct PropertyInfo
+{
+ QString name;
+ bool writable;
+};
+
+static QV4::ReturnedValue asManaged(const QJSManagedValue &value)
+{
+ const QJSValue jsVal = value.toJSValue();
+ const QV4::Managed *managed = QJSValuePrivate::asManagedType<QV4::Managed>(&jsVal);
+ return managed ? managed->asReturnedValue() : QV4::Encode::undefined();
+}
+
+static QJSManagedValue checkedProperty(const QJSManagedValue &value, const QString &name)
+{
+ return value.hasProperty(name) ? QJSManagedValue(value.property(name), value.engine())
+ : QJSManagedValue(QJSPrimitiveUndefined(), value.engine());
+}
+
+QList<PropertyInfo> getPropertyInfos(const QJSManagedValue &value)
+{
+ QV4::Scope scope(value.engine()->handle());
+ QV4::ScopedObject scoped(scope, asManaged(value));
+ if (!scoped)
+ return {};
+
+ QList<PropertyInfo> infos;
+
+ QScopedPointer<QV4::OwnPropertyKeyIterator> iterator(scoped->ownPropertyKeys(scoped));
+ QV4::Scoped<QV4::InternalClass> internalClass(scope, scoped->internalClass());
+
+ for (auto key = iterator->next(scoped); key.isValid(); key = iterator->next(scoped)) {
+ if (key.isSymbol())
+ continue;
+
+ const auto *entry = internalClass->d()->propertyTable.lookup(key);
+ infos.append({
+ key.toQString(),
+ !entry || internalClass->d()->propertyData.at(entry->index).isWritable()
+ });
+ };
+
+ return infos;
+}
+
+struct State {
+ QMap<QString, QJSValue> constructors;
+ QMap<QString, QJSValue> prototypes;
+ QSet<QString> primitives;
+};
+
+static QString buildConstructor(const QJSManagedValue &constructor, QJsonArray *classes,
+ State *seen, const QString &name, QJSManagedValue *constructed);
+
+static QString findClassName(const QJSManagedValue &value)
+{
+ if (value.isUndefined())
+ return QStringLiteral("undefined");
+ if (value.isBoolean())
+ return QStringLiteral("boolean");
+ if (value.isNumber())
+ return QStringLiteral("number");
+ if (value.isString())
+ return QStringLiteral("string");
+ if (value.isSymbol())
+ return QStringLiteral("symbol");
+
+ QV4::Scope scope(value.engine()->handle());
+ if (QV4::ScopedValue scoped(scope, asManaged(value)); scoped->isManaged())
+ return scoped->managed()->vtable()->className;
+
+ Q_UNREACHABLE_RETURN(QString());
+}
+
+static QString buildClass(const QJSManagedValue &value, QJsonArray *classes,
+ State *seen, const QString &name)
+{
+ if (value.isNull())
+ return QString();
+
+ if (seen->primitives.contains(name))
+ return name;
+ else if (name.at(0).isLower())
+ seen->primitives.insert(name);
+
+ QJsonObject classObject;
+ QV4::Scope scope(value.engine()->handle());
+
+ classObject[QStringLiteral("className")] = name;
+ classObject[QStringLiteral("qualifiedClassName")] = name;
+
+ classObject[QStringLiteral("classInfos")] = QJsonArray({
+ QJsonObject({
+ { QStringLiteral("name"), QStringLiteral("QML.Element") },
+ { QStringLiteral("value"), QStringLiteral("anonymous") }
+ })
+ });
+
+ if (value.isObject() || value.isFunction())
+ classObject[QStringLiteral("object")] = true;
+ else
+ classObject[QStringLiteral("gadget")] = true;
+
+ const QJSManagedValue prototype = value.prototype();
+
+ if (!prototype.isNull()) {
+ QString protoName;
+ for (auto it = seen->prototypes.begin(), end = seen->prototypes.end(); it != end; ++it) {
+ if (prototype.strictlyEquals(QJSManagedValue(*it, value.engine()))) {
+ protoName = it.key();
+ break;
+ }
+ }
+
+ if (protoName.isEmpty()) {
+ if (name.endsWith(QStringLiteral("ErrorPrototype"))
+ && name != QStringLiteral("ErrorPrototype")) {
+ protoName = QStringLiteral("ErrorPrototype");
+ } else if (name.endsWith(QStringLiteral("Prototype"))) {
+ protoName = findClassName(prototype);
+ if (!protoName.endsWith(QStringLiteral("Prototype")))
+ protoName += QStringLiteral("Prototype");
+ } else {
+ protoName = name.at(0).toUpper() + name.mid(1) + QStringLiteral("Prototype");
+ }
+
+ auto it = seen->prototypes.constFind(protoName);
+ if (it == seen->prototypes.cend()) {
+ seen->prototypes.insert(protoName, prototype.toJSValue());
+ buildClass(prototype, classes, seen, protoName);
+ } else if (!it->strictlyEquals(prototype.toJSValue())) {
+ qWarning() << "Cannot find a distinct name for the prototype of" << name;
+ qWarning() << protoName << "is already in use.";
+ }
+ }
+
+ classObject[QStringLiteral("superClasses")] = QJsonArray {
+ QJsonObject ({
+ { QStringLiteral("access"), QStringLiteral("public") },
+ { QStringLiteral("name"), protoName }
+ })};
+ }
+
+ QJsonArray properties, methods;
+
+ auto defineProperty = [&](const QJSManagedValue &prop, const PropertyInfo &info) {
+ QJsonObject propertyObject;
+ propertyObject.insert(QStringLiteral("name"), info.name);
+
+ // Insert faux member entry if we're allowed to write to this
+ if (info.writable)
+ propertyObject.insert(QStringLiteral("member"), QStringLiteral("fakeMember"));
+
+ if (!prop.isUndefined() && !prop.isNull()) {
+ QString propClassName = findClassName(prop);
+ if (!propClassName.at(0).isLower() && info.name != QStringLiteral("prototype")) {
+ propClassName = (name == QStringLiteral("GlobalObject"))
+ ? QString()
+ : name.at(0).toUpper() + name.mid(1);
+
+ propClassName += info.name.at(0).toUpper() + info.name.mid(1);
+ propertyObject.insert(QStringLiteral("type"),
+ buildClass(prop, classes, seen, propClassName));
+ } else {
+ // If it's the "prototype" property we just refer to generic "Object",
+ // and if it's a value type, we handle it separately.
+ propertyObject.insert(QStringLiteral("type"), propClassName);
+ }
+ }
+ return propertyObject;
+ };
+
+ QList<PropertyInfo> unRetrievedProperties;
+ QJSManagedValue constructed;
+ for (const PropertyInfo &info : getPropertyInfos(value)) {
+ QJSManagedValue prop = checkedProperty(value, info.name);
+ if (prop.engine()->hasError()) {
+ unRetrievedProperties.append(info);
+ prop.engine()->catchError();
+ continue;
+ }
+
+ // Method or constructor
+ if (prop.isFunction()) {
+ QV4::Scoped<QV4::FunctionObject> propFunction(scope, asManaged(prop));
+
+ QJsonObject methodObject;
+
+ methodObject.insert(QStringLiteral("access"), QStringLiteral("public"));
+ methodObject.insert(QStringLiteral("name"), info.name);
+ methodObject.insert(QStringLiteral("isJavaScriptFunction"), true);
+
+ const int formalParams = propFunction->getLength();
+ if (propFunction->isConstructor()) {
+ methodObject.insert(QStringLiteral("isConstructor"), true);
+
+ QString ctorName;
+ if (info.name.at(0).isUpper()) {
+ ctorName = info.name;
+ } else if (info.name == QStringLiteral("constructor")) {
+ if (name.endsWith(QStringLiteral("Prototype")))
+ ctorName = name.chopped(strlen("Prototype"));
+ else if (name.endsWith(QStringLiteral("PrototypeMember")))
+ ctorName = name.chopped(strlen("PrototypeMember"));
+ else
+ ctorName = name;
+
+ if (!ctorName.endsWith(QStringLiteral("Constructor")))
+ ctorName += QStringLiteral("Constructor");
+ }
+
+ methodObject.insert(
+ QStringLiteral("returnType"),
+ buildConstructor(prop, classes, seen, ctorName, &constructed));
+ }
+
+ QJsonArray arguments;
+ for (int i = 0; i < formalParams; i++)
+ arguments.append(QJsonObject {});
+
+ methodObject.insert(QStringLiteral("arguments"), arguments);
+
+ methods.append(methodObject);
+
+ continue;
+ }
+
+ // ...else it's just a property
+ properties.append(defineProperty(prop, info));
+ }
+
+ for (const PropertyInfo &info : unRetrievedProperties) {
+ QJSManagedValue prop = checkedProperty(
+ constructed.isUndefined() ? value : constructed, info.name);
+ if (prop.engine()->hasError()) {
+ qWarning() << "Cannot retrieve property " << info.name << "of" << name << constructed.toString();
+ qWarning().noquote() << " " << prop.engine()->catchError().toString();
+ }
+
+ properties.append(defineProperty(prop, info));
+ }
+
+ classObject[QStringLiteral("properties")] = properties;
+ classObject[QStringLiteral("methods")] = methods;
+
+ classes->append(classObject);
+
+ return name;
+}
+
+static QString buildConstructor(const QJSManagedValue &constructor, QJsonArray *classes,
+ State *seen, const QString &name, QJSManagedValue *constructed)
+{
+ QJSEngine *engine = constructor.engine();
+
+ // If the constructor appears in the global object, use the name from there.
+ const QJSManagedValue globalObject(engine->globalObject(), engine);
+ const auto infos = getPropertyInfos(globalObject);
+ for (const auto &info : infos) {
+ const QJSManagedValue member(globalObject.property(info.name), engine);
+ if (member.strictlyEquals(constructor) && info.name != name)
+ return buildConstructor(constructor, classes, seen, info.name, constructed);
+ }
+
+ if (name == QStringLiteral("Symbol"))
+ return QStringLiteral("undefined"); // Cannot construct symbols with "new";
+
+ if (name == QStringLiteral("URL")) {
+ *constructed = QJSManagedValue(
+ constructor.callAsConstructor({ QJSValue(QStringLiteral("http://a.bc")) }),
+ engine);
+ } else if (name == QStringLiteral("Promise")) {
+ *constructed = QJSManagedValue(
+ constructor.callAsConstructor(
+ { engine->evaluate(QStringLiteral("(function() {})")) }),
+ engine);
+ } else if (name == QStringLiteral("DataView")) {
+ *constructed = QJSManagedValue(
+ constructor.callAsConstructor(
+ { engine->evaluate(QStringLiteral("new ArrayBuffer()")) }),
+ engine);
+ } else if (name == QStringLiteral("Proxy")) {
+ *constructed = QJSManagedValue(constructor.callAsConstructor(
+ { engine->newObject(), engine->newObject() }), engine);
+ } else {
+ *constructed = QJSManagedValue(constructor.callAsConstructor(), engine);
+ }
+
+ if (engine->hasError()) {
+ qWarning() << "Calling constructor" << name << "failed";
+ qWarning().noquote() << " " << engine->catchError().toString();
+ return QString();
+ } else if (name.isEmpty()) {
+ Q_UNREACHABLE();
+ }
+
+ auto it = seen->constructors.constFind(name);
+ if (it == seen->constructors.cend()) {
+ seen->constructors.insert(name, constructor.toJSValue());
+ return buildClass(*constructed, classes, seen, name);
+ } else if (!constructor.strictlyEquals(QJSManagedValue(*it, constructor.engine()))) {
+ qWarning() << "Two constructors of the same name seen:" << name;
+ }
+ return name;
+}
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication app(argc, argv);
+ QCoreApplication::setApplicationVersion(QLatin1String(QT_VERSION_STR));
+
+ QCommandLineParser parser;
+ parser.addHelpOption();
+ parser.setApplicationDescription("Internal development tool.");
+ parser.addPositionalArgument("path", "Output json path.", "path");
+
+ parser.process(app);
+
+ const QStringList args = parser.positionalArguments();
+ if (auto size = args.size(); size == 0) {
+ qWarning().noquote().nospace() << app.applicationName() << ": Output path missing.";
+ return EXIT_FAILURE;
+ } else if (size >= 2) {
+ qWarning().noquote().nospace() << app.applicationName() << ": Too many output paths given. Only one allowed.";
+ }
+
+ const QString fileName = args.at(0);
+
+ QJSEngine engine;
+ engine.installExtensions(QJSEngine::AllExtensions);
+
+ QJsonArray classesArray;
+ State seen;
+
+ // object. Do this first to claim the "Object" name for the prototype.
+ buildClass(QJSManagedValue(engine.newObject(), &engine), &classesArray, &seen,
+ QStringLiteral("object"));
+
+
+ buildClass(QJSManagedValue(engine.globalObject(), &engine), &classesArray, &seen,
+ QStringLiteral("GlobalObject"));
+
+ // Add JS types, in case they aren't used anywhere.
+
+
+ // function
+ buildClass(QJSManagedValue(engine.evaluate(QStringLiteral("(function() {})")), &engine),
+ &classesArray, &seen, QStringLiteral("function"));
+
+ // string
+ buildClass(QJSManagedValue(QStringLiteral("s"), &engine), &classesArray, &seen,
+ QStringLiteral("string"));
+
+ // undefined
+ buildClass(QJSManagedValue(QJSPrimitiveUndefined(), &engine), &classesArray, &seen,
+ QStringLiteral("undefined"));
+
+ // number
+ buildClass(QJSManagedValue(QJSPrimitiveValue(1.1), &engine), &classesArray, &seen,
+ QStringLiteral("number"));
+
+ // boolean
+ buildClass(QJSManagedValue(QJSPrimitiveValue(true), &engine), &classesArray, &seen,
+ QStringLiteral("boolean"));
+
+ // symbol
+ buildClass(QJSManagedValue(engine.newSymbol(QStringLiteral("s")), &engine),
+ &classesArray, &seen, QStringLiteral("symbol"));
+
+ // Generate the fake metatypes json structure
+ QJsonDocument metatypesJson = QJsonDocument(
+ QJsonArray({
+ QJsonObject({
+ {QStringLiteral("classes"), classesArray}
+ })
+ })
+ );
+
+ QFile file(fileName);
+ if (!file.open(QFile::WriteOnly)) {
+ qWarning() << "Failed to write metatypes json to" << fileName;
+ return 1;
+ }
+
+ file.write(metatypesJson.toJson());
+ file.close();
+
+ return 0;
+}
diff --git a/tools/qmllint/.prev_CMakeLists.txt b/tools/qmllint/.prev_CMakeLists.txt
deleted file mode 100644
index 3b5a152f4b..0000000000
--- a/tools/qmllint/.prev_CMakeLists.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-# Generated from qmllint.pro.
-
-#####################################################################
-## qmllint Tool:
-#####################################################################
-
-qt_get_tool_target_name(target_name qmllint)
-qt_internal_add_tool(${target_name}
- TARGET_DESCRIPTION "QML Syntax Verifier"
- SOURCES
- checkidentifiers.cpp checkidentifiers.h
- findwarnings.cpp findwarnings.h
- main.cpp
- qcoloroutput.cpp qcoloroutput.h
- PUBLIC_LIBRARIES
- Qt::CorePrivate
- Qt::QmlCompilerPrivate
- Qt::QmlDevToolsPrivate
-)
-
-#### Keys ignored in scope 1:.:.:qmllint.pro:<TRUE>:
-# QMAKE_TARGET_DESCRIPTION = "QML" "Syntax" "Verifier"
-# _OPTION = "host_build"
diff --git a/tools/qmllint/CMakeLists.txt b/tools/qmllint/CMakeLists.txt
index a133af3cb7..ae3de5901d 100644
--- a/tools/qmllint/CMakeLists.txt
+++ b/tools/qmllint/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qmllint.pro.
#####################################################################
@@ -9,15 +12,13 @@ qt_internal_add_tool(${target_name}
TARGET_DESCRIPTION "QML Syntax Verifier"
TOOLS_TARGET Qml # special case
SOURCES
- checkidentifiers.cpp checkidentifiers.h
- findwarnings.cpp findwarnings.h
main.cpp
- qcoloroutput.cpp qcoloroutput.h
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::CorePrivate
Qt::QmlCompilerPrivate
- Qt::QmlDevToolsPrivate
+ Qt::QmlToolingSettingsPrivate
)
+qt_internal_return_unless_building_tools()
#### Keys ignored in scope 1:.:.:qmllint.pro:<TRUE>:
# QMAKE_TARGET_DESCRIPTION = "QML" "Syntax" "Verifier"
diff --git a/tools/qmllint/checkidentifiers.cpp b/tools/qmllint/checkidentifiers.cpp
deleted file mode 100644
index b0c56bcec6..0000000000
--- a/tools/qmllint/checkidentifiers.cpp
+++ /dev/null
@@ -1,465 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "checkidentifiers.h"
-#include "qcoloroutput.h"
-
-#include <QtCore/qqueue.h>
-#include <QtCore/qsharedpointer.h>
-#include <stack>
-
-class IssueLocationWithContext
-{
-public:
- IssueLocationWithContext(const QString &code, const QQmlJS::SourceLocation &location) {
- int before = qMax(0,code.lastIndexOf(QLatin1Char('\n'), location.offset));
- m_beforeText = QStringView{code}.mid(before + 1, int(location.offset - (before + 1)));
- m_issueText = QStringView{code}.mid(location.offset, location.length);
- int after = code.indexOf(QLatin1Char('\n'), int(location.offset + location.length));
- m_afterText = QStringView{code}.mid(int(location.offset + location.length),
- int(after - (location.offset+location.length)));
- }
-
- QStringView beforeText() const { return m_beforeText; }
- QStringView issueText() const { return m_issueText; }
- QStringView afterText() const { return m_afterText; }
-
-private:
- QStringView m_beforeText;
- QStringView m_issueText;
- QStringView m_afterText;
-};
-
-static const QStringList unknownBuiltins = {
- QStringLiteral("alias"), // TODO: we cannot properly resolve aliases, yet
- QStringLiteral("QJSValue"), // We cannot say anything intelligent about untyped JS values.
-
- // Same for generic variants
- QStringLiteral("variant"),
- QStringLiteral("var")
-};
-
-void CheckIdentifiers::printContext(
- const QString &code, ColorOutput *output, const QQmlJS::SourceLocation &location)
-{
- IssueLocationWithContext issueLocationWithContext { code, location };
- output->write(issueLocationWithContext.beforeText().toString(), Normal);
- output->write(issueLocationWithContext.issueText().toString(), Error);
- output->write(issueLocationWithContext.afterText().toString() + QLatin1Char('\n'), Normal);
- int tabCount = issueLocationWithContext.beforeText().count(QLatin1Char('\t'));
- output->write(QString::fromLatin1(" ").repeated(
- issueLocationWithContext.beforeText().length() - tabCount)
- + QString::fromLatin1("\t").repeated(tabCount)
- + QString::fromLatin1("^").repeated(location.length)
- + QLatin1Char('\n'), Normal);
-}
-
-template<typename Visitor>
-static bool walkRelatedScopes(QQmlJSScope::ConstPtr rootType, const Visitor &visit)
-{
- if (rootType.isNull())
- return false;
- std::stack<QQmlJSScope::ConstPtr> stack;
- stack.push(rootType);
-
- while (!stack.empty()) {
- const auto type = stack.top();
- stack.pop();
-
- if (visit(type))
- return true;
-
- if (auto attachedType = type->attachedType())
- stack.push(attachedType);
-
- if (auto baseType = type->baseType())
- stack.push(baseType);
-
- // Push extension type last. It overrides the base type.
- if (auto extensionType = type->extensionType())
- stack.push(extensionType);
- }
-
- return false;
-}
-
-bool CheckIdentifiers::checkMemberAccess(const QVector<FieldMember> &members,
- const QQmlJSScope::ConstPtr &outerScope,
- const QQmlJSMetaProperty *prop) const
-{
-
- QStringList expectedNext;
- QString detectedRestrictiveName;
- QString detectedRestrictiveKind;
-
- if (prop != nullptr && prop->isList()) {
- detectedRestrictiveKind = QLatin1String("list");
- expectedNext.append(QLatin1String("length"));
- }
-
- QQmlJSScope::ConstPtr scope = outerScope;
- for (const FieldMember &access : members) {
- if (scope.isNull()) {
- m_colorOut->writePrefixedMessage(
- QString::fromLatin1("Type \"%1\" of base \"%2\" not found when accessing member \"%3\" at %4:%5:%6.\n")
- .arg(detectedRestrictiveKind)
- .arg(detectedRestrictiveName)
- .arg(access.m_name)
- .arg(m_fileName)
- .arg(access.m_location.startLine)
- .arg(access.m_location.startColumn), Warning);
- printContext(m_code, m_colorOut, access.m_location);
- return false;
- }
-
- if (!detectedRestrictiveKind.isEmpty()) {
- if (expectedNext.contains(access.m_name)) {
- expectedNext.clear();
- continue;
- }
-
- m_colorOut->writePrefixedMessage(QString::fromLatin1(
- "\"%1\" is a %2. You cannot access \"%3\" on it at %4:%5:%6\n")
- .arg(detectedRestrictiveName)
- .arg(detectedRestrictiveKind)
- .arg(access.m_name)
- .arg(m_fileName)
- .arg(access.m_location.startLine)
- .arg(access.m_location.startColumn), Warning);
- printContext(m_code, m_colorOut, access.m_location);
- return false;
- }
-
- const auto property = scope->property(access.m_name);
- if (!property.propertyName().isEmpty()) {
- const QString typeName = access.m_parentType.isEmpty() ? property.typeName()
- : access.m_parentType;
- if (property.isList()) {
- detectedRestrictiveKind = QLatin1String("list");
- detectedRestrictiveName = access.m_name;
- expectedNext.append(QLatin1String("length"));
- continue;
- }
-
- if (typeName == QLatin1String("string")) {
- detectedRestrictiveKind = typeName;
- detectedRestrictiveName = access.m_name;
- expectedNext.append(QLatin1String("length"));
- continue;
- }
-
- if (access.m_parentType.isEmpty()) {
- scope = property.type();
- if (scope.isNull()) {
- // Properties should always have a type. Otherwise something
- // was missing from the import already.
- detectedRestrictiveKind = typeName;
- detectedRestrictiveName = access.m_name;
- }
- continue;
- }
-
- if (unknownBuiltins.contains(typeName))
- return true;
-
- const auto it = m_types.find(typeName);
- if (it == m_types.end()) {
- detectedRestrictiveKind = typeName;
- detectedRestrictiveName = access.m_name;
- scope = QQmlJSScope::ConstPtr();
- } else {
- scope = *it;
- }
-
- continue;
- }
-
- if (scope->hasMethod(access.m_name))
- return true; // Access to property of JS function
-
- auto checkEnums = [&](const QQmlJSScope::ConstPtr &scope) {
- if (scope->hasEnumeration(access.m_name)) {
- detectedRestrictiveKind = QLatin1String("enum");
- detectedRestrictiveName = access.m_name;
- expectedNext.append(scope->enumeration(access.m_name).keys());
- return true;
- }
-
- if (scope->hasEnumerationKey(access.m_name)) {
- detectedRestrictiveKind = QLatin1String("enum");
- detectedRestrictiveName = access.m_name;
- return true;
- }
-
- return false;
- };
-
- checkEnums(scope);
-
- if (!detectedRestrictiveName.isEmpty())
- continue;
-
- QQmlJSScope::ConstPtr rootType;
- if (!access.m_parentType.isEmpty())
- rootType = m_types.value(access.m_parentType);
- else
- rootType = scope;
-
- bool typeFound =
- walkRelatedScopes(rootType, [&](QQmlJSScope::ConstPtr type) {
- const auto typeProperties = type->ownProperties();
- const auto typeIt = typeProperties.find(access.m_name);
- if (typeIt != typeProperties.end()) {
- scope = typeIt->type();
- return true;
- }
-
- const auto typeMethods = type->ownMethods();
- const auto typeMethodIt = typeMethods.find(access.m_name);
- if (typeMethodIt != typeMethods.end()) {
- detectedRestrictiveName = access.m_name;
- detectedRestrictiveKind = QLatin1String("method");
- return true;
- }
-
- return checkEnums(type);
- });
- if (typeFound)
- continue;
-
- if (access.m_name.front().isUpper() && scope->scopeType() == QQmlJSScope::QMLScope) {
- // may be an attached type
- const auto it = m_types.find(access.m_name);
- if (it != m_types.end() && *it && !(*it)->attachedTypeName().isEmpty()) {
- if (const auto attached = (*it)->attachedType()) {
- scope = attached;
- continue;
- }
- }
- }
-
- m_colorOut->writePrefixedMessage(QString::fromLatin1(
- "Property \"%1\" not found on type \"%2\" at %3:%4:%5\n")
- .arg(access.m_name)
- .arg(scope->internalName().isEmpty()
- ? scope->baseTypeName() : scope->internalName())
- .arg(m_fileName)
- .arg(access.m_location.startLine)
- .arg(access.m_location.startColumn), Warning);
- printContext(m_code, m_colorOut, access.m_location);
- return false;
- }
-
- return true;
-}
-
-bool CheckIdentifiers::operator()(
- const QHash<QString, QQmlJSScope::ConstPtr> &qmlIDs,
- const QHash<QQmlJS::SourceLocation, SignalHandler> &signalHandlers,
- const MemberAccessChains &memberAccessChains,
- const QQmlJSScope::ConstPtr &root, const QString &rootId) const
-{
- bool identifiersClean = true;
-
- // revisit all scopes
- QQueue<QQmlJSScope::ConstPtr> workQueue;
- workQueue.enqueue(root);
- while (!workQueue.empty()) {
- const QQmlJSScope::ConstPtr currentScope = workQueue.dequeue();
-
- const auto scopeMemberAccessChains = memberAccessChains[currentScope];
- for (auto memberAccessChain : scopeMemberAccessChains) {
- if (memberAccessChain.isEmpty())
- continue;
-
- auto memberAccessBase = memberAccessChain.takeFirst();
- const auto jsId = currentScope->findJSIdentifier(memberAccessBase.m_name);
- if (jsId.has_value() && jsId->kind != QQmlJSScope::JavaScriptIdentifier::Injected) {
- if (memberAccessBase.m_location.end() < jsId->location.begin()) {
- m_colorOut->writePrefixedMessage(
- QStringLiteral(
- "Variable \"%1\" is used before its declaration at %2:%3. "
- "The declaration is at %4:%5.\n")
- .arg(memberAccessBase.m_name)
- .arg(memberAccessBase.m_location.startLine)
- .arg(memberAccessBase.m_location.startColumn)
- .arg(jsId->location.startLine)
- .arg(jsId->location.startColumn), Warning);
- printContext(m_code, m_colorOut, memberAccessBase.m_location);
- identifiersClean = false;
- }
- continue;
- }
-
- auto it = qmlIDs.find(memberAccessBase.m_name);
- if (it != qmlIDs.end()) {
- if (!it->isNull()) {
- if (!checkMemberAccess(memberAccessChain, *it))
- identifiersClean = false;
- continue;
- } else if (!memberAccessChain.isEmpty()) {
- // It could be a qualified type name
- const QString scopedName = memberAccessChain.first().m_name;
- if (scopedName.front().isUpper()) {
- const QString qualified = memberAccessBase.m_name + QLatin1Char('.')
- + scopedName;
- const auto typeIt = m_types.find(qualified);
- if (typeIt != m_types.end()) {
- memberAccessChain.takeFirst();
- if (!checkMemberAccess(memberAccessChain, *typeIt))
- identifiersClean = false;
- continue;
- }
- }
- }
- }
-
- auto qmlScope = QQmlJSScope::findCurrentQMLScope(currentScope);
- if (qmlScope->hasMethod(memberAccessBase.m_name)) {
- // a property of a JavaScript function, or a method
- continue;
- }
-
- const auto property = qmlScope->property(memberAccessBase.m_name);
- if (!property.propertyName().isEmpty()) {
- if (memberAccessChain.isEmpty() || unknownBuiltins.contains(property.typeName()))
- continue;
-
- if (!property.type()) {
- m_colorOut->writePrefixedMessage(QString::fromLatin1(
- "Type of property \"%2\" not found at %3:%4:%5\n")
- .arg(memberAccessBase.m_name)
- .arg(m_fileName)
- .arg(memberAccessBase.m_location.startLine)
- .arg(memberAccessBase.m_location.startColumn), Warning);
- printContext(m_code, m_colorOut, memberAccessBase.m_location);
- identifiersClean = false;
- } else if (!checkMemberAccess(memberAccessChain, property.type(), &property)) {
- identifiersClean = false;
- }
-
- continue;
- }
-
- const QString baseName = memberAccessBase.m_name;
- auto typeIt = m_types.find(memberAccessBase.m_name);
- bool baseIsPrefixed = false;
- while (typeIt != m_types.end() && typeIt->isNull()) {
- // This is a namespaced import. Check with the full name.
- if (!memberAccessChain.isEmpty()) {
- auto location = memberAccessBase.m_location;
- memberAccessBase = memberAccessChain.takeFirst();
- memberAccessBase.m_name.prepend(baseName + u'.');
- location.length = memberAccessBase.m_location.offset - location.offset
- + memberAccessBase.m_location.length;
- memberAccessBase.m_location = location;
- typeIt = m_types.find(memberAccessBase.m_name);
- baseIsPrefixed = true;
- }
- }
-
- if (typeIt != m_types.end() && !typeIt->isNull()) {
- if (!checkMemberAccess(memberAccessChain, *typeIt))
- identifiersClean = false;
- continue;
- }
-
- identifiersClean = false;
- const auto location = memberAccessBase.m_location;
-
- if (baseIsPrefixed) {
- m_colorOut->writePrefixedMessage(
- QString::fromLatin1("type not found in namespace at %1:%2:%3\n")
- .arg(m_fileName)
- .arg(location.startLine).arg(location.startColumn),
- Warning);
- } else {
- m_colorOut->writePrefixedMessage(
- QString::fromLatin1("unqualified access at %1:%2:%3\n")
- .arg(m_fileName)
- .arg(location.startLine).arg(location.startColumn),
- Warning);
- }
-
- printContext(m_code, m_colorOut, location);
-
- // root(JS) --> (first element)
- const auto firstElement = root->childScopes()[0];
- if (firstElement->hasProperty(memberAccessBase.m_name)
- || firstElement->hasMethod(memberAccessBase.m_name)
- || firstElement->hasEnumeration(memberAccessBase.m_name)) {
- m_colorOut->writePrefixedMessage(
- memberAccessBase.m_name
- + QLatin1String(" is a member of the root element\n")
- + QLatin1String(" You can qualify the access with its id "
- "to avoid this warning:\n"),
- Info, QStringLiteral("Note"));
- if (rootId == QLatin1String("<id>")) {
- m_colorOut->writePrefixedMessage(
- QLatin1String("You first have to give the root element an id\n"),
- Warning, QStringLiteral("Note"));
- }
- IssueLocationWithContext issueLocationWithContext {m_code, location};
- m_colorOut->write(issueLocationWithContext.beforeText().toString(), Normal);
- m_colorOut->write(rootId + QLatin1Char('.'), Hint);
- m_colorOut->write(issueLocationWithContext.issueText().toString(), Normal);
- m_colorOut->write(issueLocationWithContext.afterText() + QLatin1Char('\n'), Normal);
- } else if (jsId.has_value()
- && jsId->kind == QQmlJSScope::JavaScriptIdentifier::Injected) {
- const QQmlJSScope::JavaScriptIdentifier id = jsId.value();
- m_colorOut->writePrefixedMessage(
- memberAccessBase.m_name + QString::fromLatin1(
- " is accessible in this scope because "
- "you are handling a signal at %1:%2:%3\n")
- .arg(m_fileName)
- .arg(id.location.startLine).arg(id.location.startColumn),
- Info, QStringLiteral("Note"));
- m_colorOut->write(QLatin1String("Consider using a function instead\n"), Normal);
- IssueLocationWithContext context {m_code, id.location};
- m_colorOut->write(context.beforeText() + QLatin1Char(' '));
-
- const auto handler = signalHandlers[id.location];
-
- m_colorOut->write(QLatin1String(handler.isMultiline ? "function(" : "("), Hint);
- const auto parameters = handler.signal.parameterNames();
- for (int numParams = parameters.size(); numParams > 0; --numParams) {
- m_colorOut->write(parameters.at(parameters.size() - numParams), Hint);
- if (numParams > 1)
- m_colorOut->write(QLatin1String(", "), Hint);
- }
- m_colorOut->write(QLatin1String(handler.isMultiline ? ")" : ") => "), Hint);
- m_colorOut->write(QLatin1String(" {..."), Normal);
- }
- m_colorOut->write(QLatin1String("\n\n\n"), Normal);
- }
- const auto childScopes = currentScope->childScopes();
- for (auto const &childScope : childScopes)
- workQueue.enqueue(childScope);
- }
- return identifiersClean;
-}
diff --git a/tools/qmllint/checkidentifiers.h b/tools/qmllint/checkidentifiers.h
deleted file mode 100644
index f4fd8c9f14..0000000000
--- a/tools/qmllint/checkidentifiers.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef CHECKIDENTIFIERS_H
-#define CHECKIDENTIFIERS_H
-
-#include <QtQmlCompiler/private/qqmljsscope_p.h>
-#include <QtQmlCompiler/private/qqmljsimporter_p.h>
-
-class ColorOutput;
-
-struct SignalHandler {
- QQmlJSMetaMethod signal;
- bool isMultiline;
-};
-
-struct FieldMember
-{
- QString m_name;
- QString m_parentType;
- QQmlJS::SourceLocation m_location;
-};
-
-using MemberAccessChains = QHash<QQmlJSScope::ConstPtr, QVector<QVector<FieldMember>>>;
-
-class CheckIdentifiers
-{
-public:
- CheckIdentifiers(ColorOutput *colorOut, const QString &code,
- const QQmlJSImporter::ImportedTypes &types, const QString &fileName) :
- m_colorOut(colorOut), m_code(code), m_types(types), m_fileName(fileName)
- {}
-
- bool operator ()(const QHash<QString, QQmlJSScope::ConstPtr> &qmlIDs,
- const QHash<QQmlJS::SourceLocation, SignalHandler> &signalHandlers,
- const MemberAccessChains &memberAccessChains,
- const QQmlJSScope::ConstPtr &root, const QString &rootId) const;
-
- static void printContext(const QString &code, ColorOutput *output,
- const QQmlJS::SourceLocation &location);
-
-private:
- bool checkMemberAccess(const QVector<FieldMember> &members,
- const QQmlJSScope::ConstPtr &outerScope,
- const QQmlJSMetaProperty *prop = nullptr) const;
-
- ColorOutput *m_colorOut = nullptr;
- QString m_code;
- QQmlJSImporter::ImportedTypes m_types;
- QString m_fileName;
-};
-
-#endif // CHECKIDENTIFIERS_H
diff --git a/tools/qmllint/findwarnings.cpp b/tools/qmllint/findwarnings.cpp
deleted file mode 100644
index e0e1c3831e..0000000000
--- a/tools/qmllint/findwarnings.cpp
+++ /dev/null
@@ -1,591 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "findwarnings.h"
-#include "checkidentifiers.h"
-
-#include <QtQmlCompiler/private/qqmljsscope_p.h>
-#include <QtQmlCompiler/private/qqmljstypedescriptionreader_p.h>
-#include <QtQmlCompiler/private/qqmljstypereader_p.h>
-
-#include <QtQml/private/qqmljsast_p.h>
-#include <QtQml/private/qqmljslexer_p.h>
-#include <QtQml/private/qqmljsparser_p.h>
-#include <QtQml/private/qv4codegen_p.h>
-#include <QtQml/private/qqmlimportresolver_p.h>
-
-#include <QtCore/qfile.h>
-#include <QtCore/qdiriterator.h>
-#include <QtCore/qscopedvaluerollback.h>
-
-void FindWarningVisitor::checkInheritanceCycle(QQmlJSScope::ConstPtr scope)
-{
- QList<QQmlJSScope::ConstPtr> scopes;
- while (!scope.isNull()) {
- if (scopes.contains(scope)) {
- QString inheritenceCycle;
- for (const auto &seen: qAsConst(scopes)) {
- if (!inheritenceCycle.isEmpty())
- inheritenceCycle.append(QLatin1String(" -> "));
- inheritenceCycle.append(seen->baseTypeName());
- }
-
- if (m_warnInheritanceCycle) {
- m_errors.append({
- QStringLiteral("%1 is part of an inheritance cycle: %2\n")
- .arg(scope->internalName())
- .arg(inheritenceCycle),
- QtWarningMsg,
- QQmlJS::SourceLocation()
- });
- }
-
- m_unknownImports.insert(scope->internalName());
- m_visitFailed = true;
- break;
- }
-
- scopes.append(scope);
-
- if (scope->baseTypeName().isEmpty()) {
- break;
- } else if (auto newScope = scope->baseType()) {
- scope = newScope;
- } else {
- m_errors.append({
- scope->baseTypeName() + QStringLiteral(
- " was not found. Did you add all import paths?\n"),
- QtWarningMsg,
- QQmlJS::SourceLocation()
- });
- m_unknownImports.insert(scope->baseTypeName());
- m_visitFailed = true;
- break;
- }
- }
-}
-
-void FindWarningVisitor::checkGroupedAndAttachedScopes(QQmlJSScope::ConstPtr scope)
-{
- auto children = scope->childScopes();
- while (!children.isEmpty()) {
- auto childScope = children.takeFirst();
- const auto type = childScope->scopeType();
- switch (type) {
- case QQmlJSScope::GroupedPropertyScope:
- case QQmlJSScope::AttachedPropertyScope:
- if (!childScope->baseType()) {
- m_errors.append({
- QStringLiteral("unknown %1 property scope %2.")
- .arg(type == QQmlJSScope::GroupedPropertyScope
- ? QStringLiteral("grouped")
- : QStringLiteral("attached"),
- childScope->internalName()),
- QtWarningMsg,
- childScope->sourceLocation()
- });
- m_visitFailed = true;
- }
- children.append(childScope->childScopes());
- default:
- break;
- }
- }
-}
-
-void FindWarningVisitor::flushPendingSignalParameters()
-{
- const SignalHandler handler = m_signalHandlers[m_pendingSingalHandler];
- for (const QString &parameter : handler.signal.parameterNames()) {
- m_currentScope->insertJSIdentifier(
- parameter, {
- QQmlJSScope::JavaScriptIdentifier::Injected,
- m_pendingSingalHandler
- });
- }
- m_pendingSingalHandler = QQmlJS::SourceLocation();
-}
-
-void FindWarningVisitor::throwRecursionDepthError()
-{
- QQmlJSImportVisitor::throwRecursionDepthError();
- m_visitFailed = true;
-}
-
-bool FindWarningVisitor::visit(QQmlJS::AST::ExpressionStatement *ast)
-{
- if (m_pendingSingalHandler.isValid()) {
- enterEnvironment(QQmlJSScope::JSFunctionScope, "signalhandler", ast->firstSourceLocation());
- flushPendingSignalParameters();
- }
- return true;
-}
-
-void FindWarningVisitor::endVisit(QQmlJS::AST::ExpressionStatement *)
-{
- if (m_currentScope->scopeType() == QQmlJSScope::JSFunctionScope
- && m_currentScope->baseTypeName() == "signalhandler") {
- leaveEnvironment();
- }
-}
-
-bool FindWarningVisitor::visit(QQmlJS::AST::Block *block)
-{
- if (!QQmlJSImportVisitor::visit(block))
- return false;
- if (m_pendingSingalHandler.isValid())
- flushPendingSignalParameters();
- return true;
-}
-
-bool FindWarningVisitor::visit(QQmlJS::AST::WithStatement *withStatement)
-{
- if (m_warnWithStatement) {
- m_errors.append({
- QStringLiteral(
- "with statements are strongly discouraged in QML "
- "and might cause false positives when analysing unqualified "
- "identifiers\n"),
- QtWarningMsg,
- withStatement->firstSourceLocation()
- });
- }
-
- return QQmlJSImportVisitor::visit(withStatement);
-}
-
-static QString signalName(QStringView handlerName)
-{
- if (handlerName.startsWith(u"on") && handlerName.size() > 2) {
- QString signal = handlerName.mid(2).toString();
- for (int i = 0; i < signal.length(); ++i) {
- QChar &ch = signal[i];
- if (ch.isLower())
- return QString();
- if (ch.isUpper()) {
- ch = ch.toLower();
- return signal;
- }
- }
- }
- return QString();
-}
-
-bool FindWarningVisitor::visit(QQmlJS::AST::UiScriptBinding *uisb)
-{
- using namespace QQmlJS::AST;
-
- const auto qmlScope = m_currentScope;
- if (!QQmlJSImportVisitor::visit(uisb))
- return false;
-
- auto name = uisb->qualifiedId->name;
- if (name == QLatin1String("id")) {
- // Figure out whether the current scope is the root scope.
- if (auto parentScope = qmlScope->parentScope()) {
- if (!parentScope->parentScope()) {
- const auto expstat = cast<ExpressionStatement *>(uisb->statement);
- const auto identexp = cast<IdentifierExpression *>(expstat->expression);
- m_rootId = identexp->name.toString();
- }
- }
- return true;
- }
-
- const QString signal = signalName(name);
- if (signal.isEmpty()) {
- for (const auto &childScope : qmlScope->childScopes()) {
- if ((childScope->scopeType() == QQmlJSScope::AttachedPropertyScope
- || childScope->scopeType() == QQmlJSScope::GroupedPropertyScope)
- && childScope->internalName() == name) {
- return true;
- }
- }
-
- if (!qmlScope->hasProperty(name.toString())) {
- m_errors.append({
- QStringLiteral("Binding assigned to \"%1\", but no property \"%1\" "
- "exists in the current element.\n").arg(name),
- QtWarningMsg,
- uisb->firstSourceLocation()
- });
- m_visitFailed = true;
- return true;
- }
-
- const auto property = qmlScope->property(name.toString());
- if (!property.type()) {
- m_errors.append({
- QStringLiteral("No type found for property \"%1\". This may be due "
- "to a missing import statement or incomplete "
- "qmltypes files.\n").arg(name),
- QtWarningMsg,
- uisb->firstSourceLocation()
- });
- m_visitFailed = true;
-
- }
-
- return true;
- }
-
-
- if (!qmlScope->hasMethod(signal) && m_warnUnqualified) {
- m_errors.append({
- QStringLiteral("no matching signal found for handler \"%1\"\n")
- .arg(name.toString()),
- QtWarningMsg,
- uisb->firstSourceLocation()
- });
- m_visitFailed = true;
- return true;
- }
-
- QQmlJSMetaMethod scopeSignal;
- for (QQmlJSScope::ConstPtr scope = qmlScope; scope; scope = scope->baseType()) {
- const auto methods = scope->ownMethods();
- const auto methodsRange = methods.equal_range(signal);
- for (auto method = methodsRange.first; method != methodsRange.second; ++method) {
- if (method->methodType() != QQmlJSMetaMethod::Signal)
- continue;
- scopeSignal = *method;
- break;
- }
- }
-
- const auto statement = uisb->statement;
- if (ExpressionStatement *expr = cast<ExpressionStatement *>(statement)) {
- if (FunctionExpression *func = expr->expression->asFunctionDefinition()) {
- // functions are already handled
- // they do not get names inserted according to the signal, but access their formal
- // parameters. Let's still check if the names match, though.
- const QStringList signalParameters = scopeSignal.parameterNames();
- qsizetype i = 0, end = signalParameters.length();
- for (FormalParameterList *formal = func->formals;
- formal; ++i, formal = formal->next) {
- if (i == end) {
- m_errors.append({
- QStringLiteral("Signal handler for \"%2\" has more formal"
- " parameters than the signal it handles.")
- .arg(name),
- QtWarningMsg,
- uisb->firstSourceLocation()
- });
- m_visitFailed = true;
- }
-
- const QStringView handlerParameter = formal->element->bindingIdentifier;
- const qsizetype j = signalParameters.indexOf(handlerParameter);
- if (j == i || j < 0)
- continue;
-
- m_errors.append({
- QStringLiteral("Parameter %1 to signal handler for \"%2\""
- " is called \"%3\". The signal has a parameter"
- " of the same name in position %4.\n")
- .arg(i + 1).arg(name, handlerParameter).arg(j + 1),
- QtWarningMsg,
- uisb->firstSourceLocation()
- });
- m_visitFailed = true;
- }
-
- return true;
- }
- }
-
- const auto firstSourceLocation = statement->firstSourceLocation();
- bool hasMultilineStatementBody
- = statement->lastSourceLocation().startLine > firstSourceLocation.startLine;
- m_pendingSingalHandler = firstSourceLocation;
- m_signalHandlers.insert(firstSourceLocation, {scopeSignal, hasMultilineStatementBody});
- return true;
-}
-
-bool FindWarningVisitor::visit(QQmlJS::AST::IdentifierExpression *idexp)
-{
- m_memberAccessChains[m_currentScope].append(
- {{idexp->name.toString(), QString(), idexp->firstSourceLocation()}});
- m_fieldMemberBase = idexp;
- return true;
-}
-
-FindWarningVisitor::FindWarningVisitor(
- QQmlJSImporter *importer, QStringList qmltypesFiles, QString code, QString fileName,
- bool silent, bool warnUnqualified, bool warnWithStatement, bool warnInheritanceCycle)
- : QQmlJSImportVisitor(importer,
- implicitImportDirectory(fileName, importer->resourceFileMapper()),
- qmltypesFiles),
- m_code(std::move(code)),
- m_rootId(QLatin1String("<id>")),
- m_filePath(std::move(fileName)),
- m_colorOut(silent),
- m_warnUnqualified(warnUnqualified),
- m_warnWithStatement(warnWithStatement),
- m_warnInheritanceCycle(warnInheritanceCycle)
-{
- m_currentScope->setInternalName("global");
-
- // setup color output
- m_colorOut.insertMapping(Error, ColorOutput::RedForeground);
- m_colorOut.insertMapping(Warning, ColorOutput::PurpleForeground);
- m_colorOut.insertMapping(Info, ColorOutput::BlueForeground);
- m_colorOut.insertMapping(Normal, ColorOutput::DefaultColor);
- m_colorOut.insertMapping(Hint, ColorOutput::GreenForeground);
- QLatin1String jsGlobVars[] = {
- /* Not listed on the MDN page; browser and QML extensions: */
- // console/debug api
- QLatin1String("console"), QLatin1String("print"),
- // garbage collector
- QLatin1String("gc"),
- // i18n
- QLatin1String("qsTr"), QLatin1String("qsTrId"), QLatin1String("QT_TR_NOOP"),
- QLatin1String("QT_TRANSLATE_NOOP"), QLatin1String("QT_TRID_NOOP"),
- // XMLHttpRequest
- QLatin1String("XMLHttpRequest")
- };
-
- QQmlJSScope::JavaScriptIdentifier globalJavaScript = {
- QQmlJSScope::JavaScriptIdentifier::LexicalScoped,
- QQmlJS::SourceLocation()
- };
- for (const char **globalName = QV4::Compiler::Codegen::s_globalNames;
- *globalName != nullptr;
- ++globalName) {
- m_currentScope->insertJSIdentifier(QString::fromLatin1(*globalName), globalJavaScript);
- }
- for (const auto& jsGlobVar: jsGlobVars)
- m_currentScope->insertJSIdentifier(jsGlobVar, globalJavaScript);
-}
-
-static MessageColors messageColor(QtMsgType type)
-{
- switch (type) {
- case QtDebugMsg:
- return Normal;
- case QtWarningMsg:
- return Warning;
- case QtCriticalMsg:
- case QtFatalMsg:
- return Error;
- case QtInfoMsg:
- return Info;
- }
-
- return Normal;
-}
-
-bool FindWarningVisitor::check()
-{
- for (const auto &error : qAsConst(m_errors)) {
- if (error.loc.isValid()) {
- m_colorOut.writePrefixedMessage(
- QStringLiteral("%1:%2: %3")
- .arg(error.loc.startLine).arg(error.loc.startColumn).arg(error.message),
- messageColor(error.type));
- } else {
- m_colorOut.writePrefixedMessage(error.message, messageColor(error.type));
- }
- }
-
- // now that all ids are known, revisit any Connections whose target were perviously unknown
- for (auto const &outstandingConnection: m_outstandingConnections) {
- auto targetScope = m_scopesById[outstandingConnection.targetName];
- if (outstandingConnection.scope) {
- for (const auto scope = targetScope; targetScope;
- targetScope = targetScope->baseType()) {
- const auto connectionMethods = targetScope->ownMethods();
- for (const auto &method : connectionMethods)
- outstandingConnection.scope->addOwnMethod(method);
- }
- }
- QScopedValueRollback<QQmlJSScope::Ptr> rollback(m_currentScope, outstandingConnection.scope);
- outstandingConnection.uiod->initializer->accept(this);
- }
-
- if (!m_warnUnqualified)
- return true;
-
- CheckIdentifiers check(&m_colorOut, m_code, m_rootScopeImports, m_filePath);
- return check(m_scopesById, m_signalHandlers, m_memberAccessChains, m_globalScope, m_rootId)
- && !m_visitFailed;
-}
-
-bool FindWarningVisitor::visit(QQmlJS::AST::UiObjectBinding *uiob)
-{
- if (!QQmlJSImportVisitor::visit(uiob))
- return false;
-
- checkInheritanceCycle(m_currentScope);
- return true;
-}
-
-bool FindWarningVisitor::visit(QQmlJS::AST::UiObjectDefinition *uiod)
-{
- using namespace QQmlJS::AST;
-
- if (!QQmlJSImportVisitor::visit(uiod))
- return false;
-
- const QString name = m_currentScope->baseTypeName();
- if (name.isEmpty() || name.front().isLower())
- return false; // Ignore grouped properties for now
-
- checkInheritanceCycle(m_currentScope);
-
- if (name.endsWith("Connections")) {
- QString target;
- auto member = uiod->initializer->members;
- while (member) {
- if (member->member->kind == QQmlJS::AST::Node::Kind_UiScriptBinding) {
- auto asBinding = static_cast<QQmlJS::AST::UiScriptBinding*>(member->member);
- if (asBinding->qualifiedId->name == QLatin1String("target")) {
- if (asBinding->statement->kind == QQmlJS::AST::Node::Kind_ExpressionStatement) {
- auto expr = static_cast<QQmlJS::AST::ExpressionStatement*>(asBinding->statement)->expression;
- if (auto idexpr = QQmlJS::AST::cast<QQmlJS::AST::IdentifierExpression*>(expr)) {
- target = idexpr->name.toString();
- } else {
- // more complex expressions are not supported
- }
- }
- break;
- }
- }
- member = member->next;
- }
- QQmlJSScope::ConstPtr targetScope;
- if (target.isEmpty()) {
- // no target set, connection comes from parentF
- QQmlJSScope::Ptr scope = m_currentScope;
- do {
- scope = scope->parentScope(); // TODO: rename method
- } while (scope->scopeType() != QQmlJSScope::QMLScope);
- targetScope = m_rootScopeImports.value(scope->baseTypeName());
- } else {
- // there was a target, check if we already can find it
- auto scopeIt = m_scopesById.find(target);
- if (scopeIt != m_scopesById.end()) {
- targetScope = *scopeIt;
- } else {
- m_outstandingConnections.push_back({target, m_currentScope, uiod});
- return false; // visit children later once target is known
- }
- }
- for (const auto scope = targetScope; targetScope; targetScope = targetScope->baseType()) {
- const auto connectionMethods = targetScope->ownMethods();
- for (const auto &method : connectionMethods)
- m_currentScope->addOwnMethod(method);
- }
- }
- return true;
-}
-
-bool FindWarningVisitor::visit(QQmlJS::AST::PatternElement *element)
-{
- if (element->isVariableDeclaration()) {
- QQmlJS::AST::BoundNames names;
- element->boundNames(&names);
- for (const auto &name : names) {
- m_currentScope->insertJSIdentifier(
- name.id, {
- (element->scope == QQmlJS::AST::VariableScope::Var)
- ? QQmlJSScope::JavaScriptIdentifier::FunctionScoped
- : QQmlJSScope::JavaScriptIdentifier::LexicalScoped,
- element->firstSourceLocation()
- });
- }
- }
-
- return true;
-}
-
-void FindWarningVisitor::endVisit(QQmlJS::AST::UiObjectDefinition *uiod)
-{
- auto childScope = m_currentScope;
- QQmlJSImportVisitor::endVisit(uiod);
-
- if (m_warnUnqualified)
- checkGroupedAndAttachedScopes(childScope);
-
- if (m_currentScope == m_globalScope
- || m_currentScope->baseTypeName() == QStringLiteral("Component")) {
- return;
- }
-
- auto property = childScope->property(QStringLiteral("parent"));
- if (!property.propertyName().isEmpty()) {
- property.setType(QQmlJSScope::ConstPtr(m_currentScope));
- childScope->addOwnProperty(property);
- }
-}
-
-bool FindWarningVisitor::visit(QQmlJS::AST::FieldMemberExpression *)
-{
- return true;
-}
-
-void FindWarningVisitor::endVisit(QQmlJS::AST::FieldMemberExpression *fieldMember)
-{
- using namespace QQmlJS::AST;
- ExpressionNode *base = fieldMember->base;
- while (auto *nested = cast<NestedExpression *>(base))
- base = nested->expression;
-
- if (m_fieldMemberBase == base) {
- QString type;
- if (auto *binary = cast<BinaryExpression *>(base)) {
- if (binary->op == QSOperator::As) {
- if (auto *right = cast<TypeExpression *>(binary->right))
- type = right->m_type->toString();
- }
- }
-
-
- auto &chain = m_memberAccessChains[m_currentScope];
- Q_ASSERT(!chain.last().isEmpty());
- chain.last().append(FieldMember {
- fieldMember->name.toString(), type, fieldMember->identifierToken
- });
- m_fieldMemberBase = fieldMember;
- } else {
- m_fieldMemberBase = nullptr;
- }
-}
-
-bool FindWarningVisitor::visit(QQmlJS::AST::BinaryExpression *)
-{
- return true;
-}
-
-void FindWarningVisitor::endVisit(QQmlJS::AST::BinaryExpression *binExp)
-{
- if (binExp->op == QSOperator::As && m_fieldMemberBase == binExp->left)
- m_fieldMemberBase = binExp;
- else
- m_fieldMemberBase = nullptr;
-}
diff --git a/tools/qmllint/findwarnings.h b/tools/qmllint/findwarnings.h
deleted file mode 100644
index 30ee47705e..0000000000
--- a/tools/qmllint/findwarnings.h
+++ /dev/null
@@ -1,127 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef FINDUNQUALIFIED_H
-#define FINDUNQUALIFIED_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-
-#include "qcoloroutput.h"
-#include "checkidentifiers.h"
-
-#include <QtQmlCompiler/private/qqmljstypedescriptionreader_p.h>
-#include <QtQmlCompiler/private/qqmljsscope_p.h>
-#include <QtQmlCompiler/private/qqmljsimporter_p.h>
-#include <QtQmlCompiler/private/qqmljsimportvisitor_p.h>
-
-#include <QtQml/private/qqmldirparser_p.h>
-#include <QtQml/private/qqmljsastvisitor_p.h>
-#include <QtQml/private/qqmljsast_p.h>
-
-#include <QtCore/qscopedpointer.h>
-
-class FindWarningVisitor : public QQmlJSImportVisitor
-{
- Q_DISABLE_COPY_MOVE(FindWarningVisitor)
-public:
- explicit FindWarningVisitor(
- QQmlJSImporter *importer, QStringList qmltypesFiles, QString code, QString fileName,
- bool silent, bool warnUnqualified, bool warnWithStatement, bool warnInheritanceCycle);
- ~FindWarningVisitor() override = default;
- bool check();
-
-private:
- QHash<QQmlJS::SourceLocation, SignalHandler> m_signalHandlers;
- QQmlJS::SourceLocation m_pendingSingalHandler;
-
- MemberAccessChains m_memberAccessChains;
-
- QQmlJS::AST::ExpressionNode *m_fieldMemberBase = nullptr;
- QString m_code;
- QString m_rootId;
- QString m_filePath;
- QSet<QString> m_unknownImports;
- ColorOutput m_colorOut;
- bool m_visitFailed = false;
-
- bool m_warnUnqualified;
- bool m_warnWithStatement;
- bool m_warnInheritanceCycle;
-
- struct OutstandingConnection
- {
- QString targetName;
- QQmlJSScope::Ptr scope;
- QQmlJS::AST::UiObjectDefinition *uiod;
- };
-
- QVarLengthArray<OutstandingConnection, 3> m_outstandingConnections; // Connections whose target we have not encountered
-
- void checkInheritanceCycle(QQmlJSScope::ConstPtr scope);
- void checkGroupedAndAttachedScopes(QQmlJSScope::ConstPtr scope);
- void flushPendingSignalParameters();
-
- void throwRecursionDepthError() override;
-
- // work around compiler error in clang11
- using QQmlJSImportVisitor::visit;
- using QQmlJSImportVisitor::endVisit;
-
- // start block/scope handling
- bool visit(QQmlJS::AST::ExpressionStatement *ast) override;
- void endVisit(QQmlJS::AST::ExpressionStatement *ast) override;
- bool visit(QQmlJS::AST::Block *ast) override;
- bool visit(QQmlJS::AST::WithStatement *withStatement) override;
-
- /* --- end block handling --- */
-
- bool visit(QQmlJS::AST::UiObjectBinding *uiob) override;
- bool visit(QQmlJS::AST::UiObjectDefinition *uiod) override;
- void endVisit(QQmlJS::AST::UiObjectDefinition *) override;
- bool visit(QQmlJS::AST::UiScriptBinding *uisb) override;
-
- // expression handling
- bool visit(QQmlJS::AST::IdentifierExpression *idexp) override;
-
- bool visit(QQmlJS::AST::PatternElement *) override;
- bool visit(QQmlJS::AST::FieldMemberExpression *idprop) override;
- void endVisit(QQmlJS::AST::FieldMemberExpression *) override;
-
- bool visit(QQmlJS::AST::BinaryExpression *) override;
- void endVisit(QQmlJS::AST::BinaryExpression *) override;
-};
-
-#endif // FINDUNQUALIFIED_H
diff --git a/tools/qmllint/main.cpp b/tools/qmllint/main.cpp
index 3fb9e9c324..05dc667232 100644
--- a/tools/qmllint/main.cpp
+++ b/tools/qmllint/main.cpp
@@ -1,120 +1,83 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Sergio Martins <sergio.martins@kdab.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "findwarnings.h"
+// Copyright (C) 2016 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Sergio Martins <sergio.martins@kdab.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#include <QtQmlCompiler/private/qqmljsresourcefilemapper_p.h>
+#include <QtQmlToolingSettings/private/qqmltoolingsettings_p.h>
+#include <QtQmlToolingSettings/private/qqmltoolingutils_p.h>
-#include <QtQml/private/qqmljslexer_p.h>
-#include <QtQml/private/qqmljsparser_p.h>
-#include <QtQml/private/qqmljsengine_p.h>
-#include <QtQml/private/qqmljsastvisitor_p.h>
-#include <QtQml/private/qqmljsast_p.h>
+#include <QtQmlCompiler/private/qqmljsresourcefilemapper_p.h>
+#include <QtQmlCompiler/private/qqmljscompiler_p.h>
+#include <QtQmlCompiler/private/qqmljslinter_p.h>
+#include <QtQmlCompiler/private/qqmljsloggingutils_p.h>
#include <QtCore/qdebug.h>
#include <QtCore/qfile.h>
#include <QtCore/qfileinfo.h>
#include <QtCore/qcoreapplication.h>
#include <QtCore/qdiriterator.h>
+#include <QtCore/qjsonobject.h>
+#include <QtCore/qjsonarray.h>
+#include <QtCore/qjsondocument.h>
+#include <QtCore/qscopeguard.h>
#if QT_CONFIG(commandlineparser)
#include <QtCore/qcommandlineparser.h>
#endif
-#ifndef QT_BOOTSTRAPPED
#include <QtCore/qlibraryinfo.h>
-#endif
-
-static bool lint_file(const QString &filename, const bool silent, const bool warnUnqualified,
- const bool warnWithStatement, const bool warnInheritanceCycle,
- const QStringList &qmlImportPaths, const QStringList &qmltypesFiles,
- const QString &resourceFile)
-{
- QFile file(filename);
- if (!file.open(QFile::ReadOnly)) {
- if (!silent)
- qWarning() << "Failed to open file" << filename << file.error();
- return false;
- }
- QString code = QString::fromUtf8(file.readAll());
- file.close();
+#include <cstdio>
- QQmlJS::Engine engine;
- QQmlJS::Lexer lexer(&engine);
-
- QFileInfo info(filename);
- const QString lowerSuffix = info.suffix().toLower();
- const bool isESModule = lowerSuffix == QLatin1String("mjs");
- const bool isJavaScript = isESModule || lowerSuffix == QLatin1String("js");
-
- lexer.setCode(code, /*lineno = */ 1, /*qmlMode=*/ !isJavaScript);
- QQmlJS::Parser parser(&engine);
-
- bool success = isJavaScript ? (isESModule ? parser.parseModule() : parser.parseProgram())
- : parser.parse();
-
- if (!success && !silent) {
- const auto diagnosticMessages = parser.diagnosticMessages();
- for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) {
- qWarning().noquote() << QString::fromLatin1("%1:%2 : %3")
- .arg(filename).arg(m.loc.startLine).arg(m.message);
- }
- }
+using namespace Qt::StringLiterals;
- if (success && !isJavaScript) {
- const auto check = [&](QQmlJSResourceFileMapper *mapper) {
- QQmlJSImporter importer(qmlImportPaths, mapper);
- FindWarningVisitor v { &importer, qmltypesFiles, code, filename, silent,
- warnUnqualified, warnWithStatement, warnInheritanceCycle };
- parser.rootNode()->accept(&v);
- success = v.check();
- };
+constexpr int JSON_LOGGING_FORMAT_REVISION = 3;
- if (resourceFile.isEmpty()) {
- check(nullptr);
+bool argumentsFromCommandLineAndFile(QStringList& allArguments, const QStringList &arguments)
+{
+ allArguments.reserve(arguments.size());
+ for (const QString &argument : arguments) {
+ // "@file" doesn't start with a '-' so we can't use QCommandLineParser for it
+ if (argument.startsWith(u'@')) {
+ QString optionsFile = argument;
+ optionsFile.remove(0, 1);
+ if (optionsFile.isEmpty()) {
+ qWarning().nospace() << "The @ option requires an input file";
+ return false;
+ }
+ QFile f(optionsFile);
+ if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ qWarning().nospace() << "Cannot open options file specified with @";
+ return false;
+ }
+ while (!f.atEnd()) {
+ QString line = QString::fromLocal8Bit(f.readLine().trimmed());
+ if (!line.isEmpty())
+ allArguments << line;
+ }
} else {
- QQmlJSResourceFileMapper mapper({ resourceFile });
- check(&mapper);
+ allArguments << argument;
}
}
-
- return success;
+ return true;
}
int main(int argv, char *argc[])
{
+ QHashSeed::setDeterministicGlobalSeed();
+ QList<QQmlJS::LoggerCategory> categories;
+
QCoreApplication app(argv, argc);
QCoreApplication::setApplicationName("qmllint");
- QCoreApplication::setApplicationVersion("1.0");
-#if QT_CONFIG(commandlineparser)
+ QCoreApplication::setApplicationVersion(QT_VERSION_STR);
QCommandLineParser parser;
- parser.setApplicationDescription(QLatin1String("QML syntax verifier"));
+ QQmlToolingSettings settings(QLatin1String("qmllint"));
+ parser.setApplicationDescription(QLatin1String(R"(QML syntax verifier and analyzer
+
+All warnings can be set to three levels:
+ disable - Fully disables the warning.
+ info - Displays the warning but does not influence the return code.
+ warning - Displays the warning and leads to a non-zero exit code if encountered.
+)"));
+
parser.addHelpOption();
parser.addVersionOption();
@@ -122,24 +85,34 @@ int main(int argv, char *argc[])
QLatin1String("Don't output syntax errors"));
parser.addOption(silentOption);
- QCommandLineOption disableCheckUnqualified(QStringList() << "no-unqualified-id",
- QLatin1String("Don't warn about unqualified identifiers"));
- parser.addOption(disableCheckUnqualified);
+ QCommandLineOption jsonOption(QStringList() << "json",
+ QLatin1String("Write output as JSON to file (or use the special "
+ "filename '-' to write to stdout)"),
+ QLatin1String("file"), QString());
+ parser.addOption(jsonOption);
- QCommandLineOption disableCheckWithStatement(QStringList() << "no-with-statement",
- QLatin1String("Don't warn about with statements"));
- parser.addOption(disableCheckWithStatement);
+ QCommandLineOption writeDefaultsOption(
+ QStringList() << "write-defaults",
+ QLatin1String("Writes defaults settings to .qmllint.ini and exits (Warning: This "
+ "will overwrite any existing settings and comments!)"));
+ parser.addOption(writeDefaultsOption);
- QCommandLineOption disableCheckInheritanceCycle(QStringList() << "no-inheritance-cycle",
- QLatin1String("Don't warn about inheritance cycles"));
+ QCommandLineOption ignoreSettings(QStringList() << "ignore-settings",
+ QLatin1String("Ignores all settings files and only takes "
+ "command line options into consideration"));
+ parser.addOption(ignoreSettings);
- parser.addOption(disableCheckInheritanceCycle);
+ QCommandLineOption moduleOption({ QStringLiteral("M"), QStringLiteral("module") },
+ QStringLiteral("Lint modules instead of files"));
+ parser.addOption(moduleOption);
QCommandLineOption resourceOption(
{ QStringLiteral("resource") },
QStringLiteral("Look for related files in the given resource file"),
QStringLiteral("resource"));
parser.addOption(resourceOption);
+ const QString &resourceSetting = QLatin1String("ResourcePath");
+ settings.addOption(resourceSetting);
QCommandLineOption qmlImportPathsOption(
QStringList() << "I"
@@ -147,72 +120,436 @@ int main(int argv, char *argc[])
QLatin1String("Look for QML modules in specified directory"),
QLatin1String("directory"));
parser.addOption(qmlImportPathsOption);
-
- QCommandLineOption qmltypesFilesOption(
+ const QString qmlImportPathsSetting = QLatin1String("AdditionalQmlImportPaths");
+ settings.addOption(qmlImportPathsSetting);
+
+ QCommandLineOption environmentOption(
+ QStringList() << "E",
+ QLatin1String("Use the QML_IMPORT_PATH environment variable to look for QML Modules"));
+ parser.addOption(environmentOption);
+
+ QCommandLineOption qmlImportNoDefault(
+ QStringList() << "bare",
+ QLatin1String("Do not include default import directories or the current directory. "
+ "This may be used to run qmllint on a project using a different Qt version."));
+ parser.addOption(qmlImportNoDefault);
+ const QString qmlImportNoDefaultSetting = QLatin1String("DisableDefaultImports");
+ settings.addOption(qmlImportNoDefaultSetting, false);
+
+ QCommandLineOption qmldirFilesOption(
QStringList() << "i"
<< "qmltypes",
- QLatin1String("Include the specified qmltypes files. By default, all qmltypes files "
- "found in the current directory are used. When this option is set, you "
- "have to explicitly add files from the current directory if you want "
- "them to be used."),
- QLatin1String("qmltypes"));
- parser.addOption(qmltypesFilesOption);
+ QLatin1String("Import the specified qmldir files. By default, the qmldir file found "
+ "in the current directory is used if present. If no qmldir file is found,"
+ "but qmltypes files are, those are imported instead. When this option is "
+ "set, you have to explicitly add the qmldir or any qmltypes files in the "
+ "current directory if you want it to be used. Importing qmltypes files "
+ "without their corresponding qmldir file is inadvisable."),
+ QLatin1String("qmldirs"));
+ parser.addOption(qmldirFilesOption);
+ const QString qmldirFilesSetting = QLatin1String("OverwriteImportTypes");
+ settings.addOption(qmldirFilesSetting);
+
+ QCommandLineOption absolutePath(
+ QStringList() << "absolute-path",
+ QLatin1String("Use absolute paths for logging instead of relative ones."));
+ absolutePath.setFlags(QCommandLineOption::HiddenFromHelp);
+ parser.addOption(absolutePath);
+
+ QCommandLineOption fixFile(QStringList() << "f"
+ << "fix",
+ QLatin1String("Automatically apply fix suggestions"));
+ parser.addOption(fixFile);
+
+ QCommandLineOption dryRun(QStringList() << "dry-run",
+ QLatin1String("Only print out the contents of the file after fix "
+ "suggestions without applying them"));
+ parser.addOption(dryRun);
+
+ QCommandLineOption listPluginsOption(QStringList() << "list-plugins",
+ QLatin1String("List all available plugins"));
+ parser.addOption(listPluginsOption);
+
+ QCommandLineOption pluginsDisable(
+ QStringList() << "D"
+ << "disable-plugins",
+ QLatin1String("List of qmllint plugins to disable (all to disable all plugins)"),
+ QLatin1String("plugins"));
+ parser.addOption(pluginsDisable);
+ const QString pluginsDisableSetting = QLatin1String("DisablePlugins");
+ settings.addOption(pluginsDisableSetting);
+
+ QCommandLineOption pluginPathsOption(
+ QStringList() << "P"
+ << "plugin-paths",
+ QLatin1String("Look for qmllint plugins in specified directory"),
+ QLatin1String("directory"));
+ parser.addOption(pluginPathsOption);
+
+ auto levelToString = [](const QQmlJS::LoggerCategory &category) -> QString {
+ Q_ASSERT(category.isIgnored() || category.level() != QtCriticalMsg);
+ if (category.isIgnored())
+ return QStringLiteral("disable");
+
+ switch (category.level()) {
+ case QtInfoMsg:
+ return QStringLiteral("info");
+ case QtWarningMsg:
+ return QStringLiteral("warning");
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ };
+
+ auto addCategory = [&](const QQmlJS::LoggerCategory &category) {
+ categories.push_back(category);
+ if (category.isDefault())
+ return;
+ QCommandLineOption option(
+ category.id().name().toString(),
+ category.description()
+ + QStringLiteral(" (default: %1)").arg(levelToString(category)),
+ QStringLiteral("level"), levelToString(category));
+ if (category.isIgnored())
+ option.setFlags(QCommandLineOption::HiddenFromHelp);
+ parser.addOption(option);
+ settings.addOption(QStringLiteral("Warnings/") + category.settingsName(),
+ levelToString(category));
+ };
+
+ for (const auto &category : QQmlJSLogger::defaultCategories()) {
+ addCategory(category);
+ }
parser.addPositionalArgument(QLatin1String("files"),
QLatin1String("list of qml or js files to verify"));
- parser.process(app);
+ QStringList arguments;
+ if (!argumentsFromCommandLineAndFile(arguments, app.arguments())) {
+ // argumentsFromCommandLine already printed any necessary warnings.
+ return 1;
+ }
+
+ if (!parser.parse(arguments)) {
+ qWarning().noquote() << parser.errorText();
+ return 1;
+ }
+
+ // Since we can't use QCommandLineParser::process(), we need to handle version and help manually
+ if (parser.isSet("version"))
+ parser.showVersion();
+
+ if (parser.isSet("help") || parser.isSet("help-all"))
+ parser.showHelp(0);
+
+ if (parser.isSet(writeDefaultsOption)) {
+ return settings.writeDefaults() ? 0 : 1;
+ }
+
+ auto updateLogLevels = [&]() {
+ for (auto &category : categories) {
+ if (category.isDefault())
+ continue;
+
+ const QString &key = category.id().name().toString();
+ const QString &settingsName = QStringLiteral("Warnings/") + category.settingsName();
+ if (parser.isSet(key) || settings.isSet(settingsName)) {
+ const QString value = parser.isSet(key) ? parser.value(key)
+ : settings.value(settingsName).toString();
+
+ // Do not try to set the levels if it's due to a default config option.
+ // This way we can tell which options have actually been overwritten by the user.
+ if (levelToString(category) == value && !parser.isSet(key))
+ continue;
+
+ if (value == "disable"_L1) {
+ category.setLevel(QtCriticalMsg);
+ category.setIgnored(true);
+ } else if (value == "info"_L1) {
+ category.setLevel(QtInfoMsg);
+ category.setIgnored(false);
+ } else if (value == "warning"_L1) {
+ category.setLevel(QtWarningMsg);
+ category.setIgnored(false);
+ } else {
+ qWarning() << "Invalid logging level" << value << "provided for"
+ << category.id().name().toString()
+ << "(allowed are: disable, info, warning)";
+ parser.showHelp(-1);
+ }
+ }
+ }
+ };
+
+ bool silent = parser.isSet(silentOption);
+ bool useAbsolutePath = parser.isSet(absolutePath);
+ bool useJson = parser.isSet(jsonOption);
+
+ // use host qml import path as a sane default if not explicitly disabled
+ QStringList defaultImportPaths = { QDir::currentPath() };
+
+ if (parser.isSet(resourceOption)) {
+ defaultImportPaths.append(QLatin1String(":/qt-project.org/imports"));
+ defaultImportPaths.append(QLatin1String(":/qt/qml"));
+ };
+
+ defaultImportPaths.append(QLibraryInfo::path(QLibraryInfo::QmlImportsPath));
+
+ QStringList qmlImportPaths =
+ parser.isSet(qmlImportNoDefault) ? QStringList {} : defaultImportPaths;
+
+ QStringList defaultQmldirFiles;
+ if (parser.isSet(qmldirFilesOption)) {
+ defaultQmldirFiles = parser.values(qmldirFilesOption);
+ } else if (!parser.isSet(qmlImportNoDefault)){
+ // If nothing given explicitly, use the qmldir file from the current directory.
+ QFileInfo qmldirFile(QStringLiteral("qmldir"));
+ if (qmldirFile.isFile()) {
+ defaultQmldirFiles.append(qmldirFile.absoluteFilePath());
+ } else {
+ // If no qmldir file is found, use the qmltypes files
+ // from the current directory for backwards compatibility.
+ QDirIterator it(".", {"*.qmltypes"}, QDir::Files);
+ while (it.hasNext()) {
+ it.next();
+ defaultQmldirFiles.append(it.fileInfo().absoluteFilePath());
+ }
+ }
+ }
+ QStringList qmldirFiles = defaultQmldirFiles;
+
+ const QStringList defaultResourceFiles =
+ parser.isSet(resourceOption) ? parser.values(resourceOption) : QStringList {};
+ QStringList resourceFiles = defaultResourceFiles;
+
+ bool success = true;
+
+ QStringList pluginPaths = { QQmlJSLinter::defaultPluginPath() };
+
+ if (parser.isSet(pluginPathsOption))
+ pluginPaths << parser.values(pluginPathsOption);
+
+ QQmlJSLinter linter(qmlImportPaths, pluginPaths, useAbsolutePath);
+
+ for (const QQmlJSLinter::Plugin &plugin : linter.plugins()) {
+ for (const QQmlJS::LoggerCategory &category : plugin.categories())
+ addCategory(category);
+ }
+
+ if (!parser.unknownOptionNames().isEmpty())
+ parser.process(app);
+
+ updateLogLevels();
+
+ if (parser.isSet(listPluginsOption)) {
+ const std::vector<QQmlJSLinter::Plugin> &plugins = linter.plugins();
+ if (!plugins.empty()) {
+ qInfo().nospace().noquote() << "Plugin\t\t\tBuilt-in?\tVersion\tAuthor\t\tDescription";
+ for (const QQmlJSLinter::Plugin &plugin : plugins) {
+ qInfo().nospace().noquote()
+ << plugin.name() << "\t\t\t" << (plugin.isBuiltin() ? "Yes" : "No")
+ << "\t\t" << plugin.version() << "\t" << plugin.author() << "\t\t"
+ << plugin.description();
+ }
+ } else {
+ qWarning() << "No plugins installed.";
+ }
+ return 0;
+ }
const auto positionalArguments = parser.positionalArguments();
if (positionalArguments.isEmpty()) {
parser.showHelp(-1);
}
- bool silent = parser.isSet(silentOption);
- bool warnUnqualified = !parser.isSet(disableCheckUnqualified);
- bool warnWithStatement = !parser.isSet(disableCheckWithStatement);
- bool warnInheritanceCycle = !parser.isSet(disableCheckInheritanceCycle);
-
- // use host qml import path as a sane default if nothing else has been provided
- QStringList qmlImportPaths = parser.isSet(qmlImportPathsOption)
- ? parser.values(qmlImportPathsOption)
-# ifndef QT_BOOTSTRAPPED
- : QStringList { QLibraryInfo::path(QLibraryInfo::QmlImportsPath), QDir::currentPath() };
-# else
- : QStringList { QDir::currentPath() };
-# endif
-
- QStringList qmltypesFiles;
- if (parser.isSet(qmltypesFilesOption)) {
- qmltypesFiles = parser.values(qmltypesFilesOption);
- } else {
- // If none are given explicitly, use the qmltypes files from the current directory.
- QDirIterator it(".", {"*.qmltypes"}, QDir::Files);
- while (it.hasNext()) {
- it.next();
- qmltypesFiles.append(it.fileInfo().absoluteFilePath());
+ QJsonArray jsonFiles;
+
+ for (const QString &filename : positionalArguments) {
+ if (!parser.isSet(ignoreSettings))
+ settings.search(filename);
+ updateLogLevels();
+
+ const QDir fileDir = QFileInfo(filename).absoluteDir();
+ auto addAbsolutePaths = [&](QStringList &list, const QStringList &entries) {
+ for (const QString &file : entries)
+ list << (QFileInfo(file).isAbsolute() ? file : fileDir.filePath(file));
+ };
+
+ resourceFiles = defaultResourceFiles;
+
+ addAbsolutePaths(resourceFiles, settings.value(resourceSetting).toStringList());
+
+ qmldirFiles = defaultQmldirFiles;
+ if (settings.isSet(qmldirFilesSetting)
+ && !settings.value(qmldirFilesSetting).toStringList().isEmpty()) {
+ qmldirFiles = {};
+ addAbsolutePaths(qmldirFiles, settings.value(qmldirFilesSetting).toStringList());
+ }
+
+ if (parser.isSet(qmlImportNoDefault)
+ || (settings.isSet(qmlImportNoDefaultSetting)
+ && settings.value(qmlImportNoDefaultSetting).toBool())) {
+ qmlImportPaths = {};
+ } else {
+ qmlImportPaths = defaultImportPaths;
+ }
+
+ if (parser.isSet(qmlImportPathsOption))
+ qmlImportPaths << parser.values(qmlImportPathsOption);
+ if (parser.isSet(environmentOption)) {
+ if (silent) {
+ qmlImportPaths << qEnvironmentVariable("QML_IMPORT_PATH")
+ .split(QDir::separator(), Qt::SkipEmptyParts)
+ << qEnvironmentVariable("QML2_IMPORT_PATH")
+ .split(QDir::separator(), Qt::SkipEmptyParts);
+ } else {
+ if (const QStringList dirsFromEnv =
+ QQmlToolingUtils::getAndWarnForInvalidDirsFromEnv(u"QML_IMPORT_PATH"_s);
+ !dirsFromEnv.isEmpty()) {
+ qInfo().nospace().noquote()
+ << "Using import directories passed from environment variable "
+ "\"QML_IMPORT_PATH\": \""
+ << dirsFromEnv.join(u"\", \""_s) << "\".";
+ qmlImportPaths << dirsFromEnv;
+ }
+ if (const QStringList dirsFromEnv =
+ QQmlToolingUtils::getAndWarnForInvalidDirsFromEnv(
+ u"QML2_IMPORT_PATH"_s);
+ !dirsFromEnv.isEmpty()) {
+ qInfo().nospace().noquote() << "Using import directories passed from the "
+ "deprecated environment variable "
+ "\"QML2_IMPORT_PATH\": \""
+ << dirsFromEnv.join(u"\", \""_s) << "\".";
+ qmlImportPaths << dirsFromEnv;
+ }
+ }
+ }
+
+ addAbsolutePaths(qmlImportPaths, settings.value(qmlImportPathsSetting).toStringList());
+
+ QSet<QString> disabledPlugins;
+
+ if (parser.isSet(pluginsDisable)) {
+ for (const QString &plugin : parser.values(pluginsDisable))
+ disabledPlugins << plugin.toLower();
+ }
+
+ if (settings.isSet(pluginsDisableSetting)) {
+ for (const QString &plugin : settings.value(pluginsDisableSetting).toStringList())
+ disabledPlugins << plugin.toLower();
+ }
+
+ linter.setPluginsEnabled(!disabledPlugins.contains("all"));
+
+ if (!linter.pluginsEnabled())
+ continue;
+
+ auto &plugins = linter.plugins();
+
+ for (auto &plugin : plugins)
+ plugin.setEnabled(!disabledPlugins.contains(plugin.name().toLower()));
+
+ const bool isFixing = parser.isSet(fixFile);
+
+ QQmlJSLinter::LintResult lintResult;
+
+ if (parser.isSet(moduleOption)) {
+ lintResult = linter.lintModule(filename, silent, useJson ? &jsonFiles : nullptr,
+ qmlImportPaths, resourceFiles);
+ } else {
+ lintResult = linter.lintFile(filename, nullptr, silent || isFixing,
+ useJson ? &jsonFiles : nullptr, qmlImportPaths,
+ qmldirFiles, resourceFiles, categories);
+ }
+ success &= (lintResult == QQmlJSLinter::LintSuccess);
+
+ if (isFixing) {
+ if (lintResult != QQmlJSLinter::LintSuccess && lintResult != QQmlJSLinter::HasWarnings)
+ continue;
+
+ QString fixedCode;
+ const QQmlJSLinter::FixResult result = linter.applyFixes(&fixedCode, silent);
+
+ if (result != QQmlJSLinter::NothingToFix && result != QQmlJSLinter::FixSuccess) {
+ success = false;
+ continue;
+ }
+
+ if (parser.isSet(dryRun)) {
+ QTextStream(stdout) << fixedCode;
+ } else {
+ if (result == QQmlJSLinter::NothingToFix) {
+ if (!silent)
+ qWarning().nospace() << "Nothing to fix in " << filename;
+ continue;
+ }
+
+ const QString backupFile = filename + u".bak"_s;
+ if (QFile::exists(backupFile) && !QFile::remove(backupFile)) {
+ if (!silent) {
+ qWarning().nospace() << "Failed to remove old backup file " << backupFile
+ << ", aborting";
+ }
+ success = false;
+ continue;
+ }
+ if (!QFile::copy(filename, backupFile)) {
+ if (!silent) {
+ qWarning().nospace()
+ << "Failed to create backup file " << backupFile << ", aborting";
+ }
+ success = false;
+ continue;
+ }
+
+ QFile file(filename);
+ if (!file.open(QIODevice::WriteOnly)) {
+ if (!silent) {
+ qWarning().nospace() << "Failed to open " << filename
+ << " for writing:" << file.errorString();
+ }
+ success = false;
+ continue;
+ }
+
+ const QByteArray data = fixedCode.toUtf8();
+ if (file.write(data) != data.size()) {
+ if (!silent) {
+ qWarning().nospace() << "Failed to write new contents to " << filename
+ << ": " << file.errorString();
+ }
+ success = false;
+ continue;
+ }
+ if (!silent) {
+ qDebug().nospace() << "Applied fixes to " << filename << ". Backup created at "
+ << backupFile;
+ }
+ }
}
}
- const QString resourceFile = parser.value(resourceOption);
+ if (useJson) {
+ QJsonObject result;
-#else
- bool silent = false;
- bool warnUnqualified = true;
- bool warnWithStatement = true;
- bool warnInheritanceCycle = true;
- QStringList qmlImportPahs {};
- QStringList qmltypesFiles {};
-#endif
- bool success = true;
-#if QT_CONFIG(commandlineparser)
- for (const QString &filename : positionalArguments)
-#else
- const auto arguments = app.arguments();
- for (const QString &filename : arguments)
-#endif
- success &= lint_file(filename, silent, warnUnqualified, warnWithStatement,
- warnInheritanceCycle, qmlImportPaths, qmltypesFiles, resourceFile);
+ result[u"revision"_s] = JSON_LOGGING_FORMAT_REVISION;
+ result[u"files"_s] = jsonFiles;
+
+ QString fileName = parser.value(jsonOption);
+
+ const QByteArray json = QJsonDocument(result).toJson(QJsonDocument::Compact);
+
+ if (fileName == u"-") {
+ QTextStream(stdout) << QString::fromUtf8(json);
+ } else {
+ QFile file(fileName);
+ if (file.open(QFile::WriteOnly))
+ file.write(json);
+ else
+ success = false;
+ }
+ }
return success ? 0 : -1;
}
diff --git a/tools/qmllint/qcoloroutput.cpp b/tools/qmllint/qcoloroutput.cpp
deleted file mode 100644
index 22783ef1b9..0000000000
--- a/tools/qmllint/qcoloroutput.cpp
+++ /dev/null
@@ -1,336 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qcoloroutput.h"
-
-#include <QtCore/qfile.h>
-#include <QtCore/qhash.h>
-
-#ifndef Q_OS_WIN
-#include <unistd.h>
-#endif
-
-class ColorOutputPrivate
-{
-public:
- ColorOutputPrivate(bool silent) : m_currentColorID(-1), m_silent(silent)
- {
- /* - QIODevice::Unbuffered because we want it to appear when the user actually calls,
- * performance is considered of lower priority.
- */
- m_out.open(stderr, QIODevice::WriteOnly | QIODevice::Unbuffered);
- m_coloringEnabled = isColoringPossible();
- }
-
- static const char *const foregrounds[];
- static const char *const backgrounds[];
-
- inline void write(const QString &msg) { m_out.write(msg.toLocal8Bit()); }
-
- static QString escapeCode(const QString &in)
- {
- const ushort escapeChar = 0x1B;
- QString result;
- result.append(QChar(escapeChar));
- result.append(QLatin1Char('['));
- result.append(in);
- result.append(QLatin1Char('m'));
- return result;
- }
-
- void insertColor(int id, ColorOutput::ColorCode code) { m_colorMapping.insert(id, code); }
- ColorOutput::ColorCode color(int id) const { return m_colorMapping.value(id); }
- bool containsColor(int id) const { return m_colorMapping.contains(id); }
-
- bool isSilent() const { return m_silent; }
- void setCurrentColorID(int colorId) { m_currentColorID = colorId; }
-
- bool coloringEnabled() const { return m_coloringEnabled; }
-
-private:
- QFile m_out;
- ColorOutput::ColorMapping m_colorMapping;
- int m_currentColorID;
- bool m_coloringEnabled;
- bool m_silent;
-
- /*!
- Returns true if it's suitable to send colored output to \c stderr.
- */
- inline bool isColoringPossible() const
- {
-#if defined(Q_OS_WIN)
- /* Windows doesn't at all support ANSI escape codes, unless
- * the user install a "device driver". See the Wikipedia links in the
- * class documentation for details. */
- return false;
-#else
- /* We use QFile::handle() to get the file descriptor. It's a bit unsure
- * whether it's 2 on all platforms and in all cases, so hopefully this layer
- * of abstraction helps handle such cases. */
- return isatty(m_out.handle());
-#endif
- }
-};
-
-const char *const ColorOutputPrivate::foregrounds[] =
-{
- "0;30",
- "0;34",
- "0;32",
- "0;36",
- "0;31",
- "0;35",
- "0;33",
- "0;37",
- "1;30",
- "1;34",
- "1;32",
- "1;36",
- "1;31",
- "1;35",
- "1;33",
- "1;37"
-};
-
-const char *const ColorOutputPrivate::backgrounds[] =
-{
- "0;40",
- "0;44",
- "0;42",
- "0;46",
- "0;41",
- "0;45",
- "0;43"
-};
-
-/*!
- \class ColorOutput
- \nonreentrant
- \brief Outputs colored messages to \c stderr.
- \internal
-
- ColorOutput is a convenience class for outputting messages to \c
- stderr using color escape codes, as mandated in ECMA-48. ColorOutput
- will only color output when it is detected to be suitable. For
- instance, if \c stderr is detected to be attached to a file instead
- of a TTY, no coloring will be done.
-
- ColorOutput does its best attempt. but it is generally undefined
- what coloring or effect the various coloring flags has. It depends
- strongly on what terminal software that is being used.
-
- When using `echo -e 'my escape sequence'`, \c{\033} works as an
- initiator but not when printing from a C++ program, despite having
- escaped the backslash. That's why we below use characters with
- value 0x1B.
-
- It can be convenient to subclass ColorOutput with a private scope,
- such that the functions are directly available in the class using
- it.
-
- \section1 Usage
-
- To output messages, call write() or writeUncolored(). write() takes
- as second argument an integer, which ColorOutput uses as a lookup
- key to find the color it should color the text in. The mapping from
- keys to colors is done using insertMapping(). Typically this is used
- by having enums for the various kinds of messages, which
- subsequently are registered.
-
- \code
- enum MyMessage
- {
- Error,
- Important
- };
-
- ColorOutput output;
- output.insertMapping(Error, ColorOutput::RedForeground);
- output.insertMapping(Import, ColorOutput::BlueForeground);
-
- output.write("This is important", Important);
- output.write("Jack, I'm only the selected official!", Error);
- \endcode
-
- \sa {http://tldp.org/HOWTO/Bash-Prompt-HOWTO/x329.html}{Bash Prompt HOWTO, 6.1. Colors},
- {http://linuxgazette.net/issue51/livingston-blade.html}{Linux Gazette, Tweaking Eterm, Edward Livingston-Blade},
- {http://www.ecma-international.org/publications/standards/Ecma-048.htm}{Standard ECMA-48, Control Functions for Coded Character Sets, ECMA International},
- {http://en.wikipedia.org/wiki/ANSI_escape_code}{Wikipedia, ANSI escape code},
- {http://linuxgazette.net/issue65/padala.html}{Linux Gazette, So You Like Color!, Pradeep Padala}
- */
-
-/*!
- \enum ColorOutput::ColorCodeComponent
- \value BlackForeground
- \value BlueForeground
- \value GreenForeground
- \value CyanForeground
- \value RedForeground
- \value PurpleForeground
- \value BrownForeground
- \value LightGrayForeground
- \value DarkGrayForeground
- \value LightBlueForeground
- \value LightGreenForeground
- \value LightCyanForeground
- \value LightRedForeground
- \value LightPurpleForeground
- \value YellowForeground
- \value WhiteForeground
- \value BlackBackground
- \value BlueBackground
- \value GreenBackground
- \value CyanBackground
- \value RedBackground
- \value PurpleBackground
- \value BrownBackground
-
- \value DefaultColor ColorOutput performs no coloring. This typically
- means black on white or white on black, depending
- on the settings of the user's terminal.
- */
-
-/*!
- Constructs a ColorOutput instance, ready for use.
- */
-ColorOutput::ColorOutput(bool silent) : d(new ColorOutputPrivate(silent)) {}
-
-// must be here so that QScopedPointer has access to the complete type
-ColorOutput::~ColorOutput() = default;
-
-/*!
- Sends \a message to \c stderr, using the color looked up in the color mapping using \a colorID.
-
- If \a color isn't available in the color mapping, result and behavior is undefined.
-
- If \a colorID is 0, which is the default value, the previously used coloring is used. ColorOutput
- is initialized to not color at all.
-
- If \a message is empty, effects are undefined.
-
- \a message will be printed as is. For instance, no line endings will be inserted.
- */
-void ColorOutput::write(const QString &message, int colorID)
-{
- if (!d->isSilent())
- d->write(colorify(message, colorID));
-}
-
-void ColorOutput::writePrefixedMessage(const QString &message, MessageColors type,
- const QString &prefix)
-{
- static const QStringList prefixes = {
- QStringLiteral("Error"),
- QStringLiteral("Warning"),
- QStringLiteral("Info"),
- QStringLiteral("Normal"),
- QStringLiteral("Hint"),
- };
-
- Q_ASSERT(prefixes.length() > qsizetype(type));
- Q_ASSERT(prefix.isEmpty() || prefix.front().isUpper());
- write((prefix.isEmpty() ? prefixes[type] : prefix) + QStringLiteral(": "), type);
- writeUncolored(message);
-}
-
-/*!
- Writes \a message to \c stderr as if for instance
- QTextStream would have been used, and adds a line ending at the end.
-
- This function can be practical to use such that one can use ColorOutput for all forms of writing.
- */
-void ColorOutput::writeUncolored(const QString &message)
-{
- if (!d->isSilent())
- d->write(message + QLatin1Char('\n'));
-}
-
-/*!
- Treats \a message and \a colorID identically to write(), but instead of writing
- \a message to \c stderr, it is prepared for being written to \c stderr, but is then
- returned.
-
- This is useful when the colored string is inserted into a translated string(dividing
- the string into several small strings prevents proper translation).
- */
-QString ColorOutput::colorify(const QString &message, int colorID) const
-{
- Q_ASSERT_X(colorID == -1 || d->containsColor(colorID), Q_FUNC_INFO,
- qPrintable(QString::fromLatin1("There is no color registered by id %1")
- .arg(colorID)));
- Q_ASSERT_X(!message.isEmpty(), Q_FUNC_INFO,
- "It makes no sense to attempt to print an empty string.");
-
- if (colorID != -1)
- d->setCurrentColorID(colorID);
-
- if (d->coloringEnabled() && colorID != -1) {
- const int color = d->color(colorID);
-
- /* If DefaultColor is set, we don't want to color it. */
- if (color & DefaultColor)
- return message;
-
- const int foregroundCode = (color & ForegroundMask) >> ForegroundShift;
- const int backgroundCode = (color & BackgroundMask) >> BackgroundShift;
- QString finalMessage;
- bool closureNeeded = false;
-
- if (foregroundCode > 0) {
- finalMessage.append(
- ColorOutputPrivate::escapeCode(
- QLatin1String(ColorOutputPrivate::foregrounds[foregroundCode - 1])));
- closureNeeded = true;
- }
-
- if (backgroundCode > 0) {
- finalMessage.append(
- ColorOutputPrivate::escapeCode(
- QLatin1String(ColorOutputPrivate::backgrounds[backgroundCode - 1])));
- closureNeeded = true;
- }
-
- finalMessage.append(message);
-
- if (closureNeeded)
- finalMessage.append(ColorOutputPrivate::escapeCode(QLatin1String("0")));
-
- return finalMessage;
- }
-
- return message;
-}
-
-/*!
- Adds a color mapping from \a colorID to \a colorCode, for this ColorOutput instance.
- */
-void ColorOutput::insertMapping(int colorID, const ColorCode colorCode)
-{
- d->insertColor(colorID, colorCode);
-}
diff --git a/tools/qmllint/qcoloroutput.h b/tools/qmllint/qcoloroutput.h
deleted file mode 100644
index 41b8751432..0000000000
--- a/tools/qmllint/qcoloroutput.h
+++ /dev/null
@@ -1,119 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QCOLOROUTPUT_H
-#define QCOLOROUTPUT_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-
-#include <QtCore/qglobal.h>
-#include <QtCore/qscopedpointer.h>
-#include <QtCore/qstring.h>
-
-class ColorOutputPrivate;
-
-enum MessageColors
-{
- Error,
- Warning,
- Info,
- Normal,
- Hint
-};
-
-class ColorOutput
-{
- enum
- {
- ForegroundShift = 10,
- BackgroundShift = 20,
- SpecialShift = 20,
- ForegroundMask = 0x1f << ForegroundShift,
- BackgroundMask = 0x7 << BackgroundShift
- };
-
-public:
- enum ColorCodeComponent
- {
- BlackForeground = 1 << ForegroundShift,
- BlueForeground = 2 << ForegroundShift,
- GreenForeground = 3 << ForegroundShift,
- CyanForeground = 4 << ForegroundShift,
- RedForeground = 5 << ForegroundShift,
- PurpleForeground = 6 << ForegroundShift,
- BrownForeground = 7 << ForegroundShift,
- LightGrayForeground = 8 << ForegroundShift,
- DarkGrayForeground = 9 << ForegroundShift,
- LightBlueForeground = 10 << ForegroundShift,
- LightGreenForeground = 11 << ForegroundShift,
- LightCyanForeground = 12 << ForegroundShift,
- LightRedForeground = 13 << ForegroundShift,
- LightPurpleForeground = 14 << ForegroundShift,
- YellowForeground = 15 << ForegroundShift,
- WhiteForeground = 16 << ForegroundShift,
-
- BlackBackground = 1 << BackgroundShift,
- BlueBackground = 2 << BackgroundShift,
- GreenBackground = 3 << BackgroundShift,
- CyanBackground = 4 << BackgroundShift,
- RedBackground = 5 << BackgroundShift,
- PurpleBackground = 6 << BackgroundShift,
- BrownBackground = 7 << BackgroundShift,
- DefaultColor = 1 << SpecialShift
- };
-
- using ColorCode = QFlags<ColorCodeComponent>;
- using ColorMapping = QHash<int, ColorCode>;
-
- ColorOutput(bool silent);
- ~ColorOutput();
-
- void insertMapping(int colorID, ColorCode colorCode);
-
- void writeUncolored(const QString &message);
- void write(const QString &message, int color = -1);
- void writePrefixedMessage(const QString &message, MessageColors type,
- const QString &prefix = QString());
- QString colorify(const QString &message, int color = -1) const;
-
-private:
- QScopedPointer<ColorOutputPrivate> d;
- Q_DISABLE_COPY_MOVE(ColorOutput)
-};
-
-Q_DECLARE_OPERATORS_FOR_FLAGS(ColorOutput::ColorCode)
-
-#endif // QCOLOROUTPUT_H
diff --git a/tools/qmlls/CMakeLists.txt b/tools/qmlls/CMakeLists.txt
new file mode 100644
index 0000000000..d61a18582e
--- /dev/null
+++ b/tools/qmlls/CMakeLists.txt
@@ -0,0 +1,23 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## qmlls Tool:
+#####################################################################
+
+qt_internal_add_app(qmlls
+ TARGET_DESCRIPTION "QML Language Server"
+ SOURCES
+ qmllanguageservertool.cpp
+ DEFINES
+ QT_USE_QSTRINGBUILDER
+ LIBRARIES
+ Qt::QmlLSPrivate
+ Qt::QmlToolingSettingsPrivate
+)
+set_target_properties(qmlls PROPERTIES WIN32_EXECUTABLE FALSE)
+
+if(NOT QT6_IS_SHARED_LIBS_BUILD)
+ qt_import_plugins(qmlls INCLUDE Qt::QmlLSQuickPlugin)
+ target_link_libraries(qmlls PRIVATE Qt::QmlLSQuickPlugin)
+endif()
diff --git a/tools/qmlls/qmllanguageservertool.cpp b/tools/qmlls/qmllanguageservertool.cpp
new file mode 100644
index 0000000000..34138638b7
--- /dev/null
+++ b/tools/qmlls/qmllanguageservertool.cpp
@@ -0,0 +1,315 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <QtQmlLS/private/qqmllanguageserver_p.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qdir.h>
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtQmlToolingSettings/private/qqmltoolingsettings_p.h>
+#include <QtQmlToolingSettings/private/qqmltoolingutils_p.h>
+#include <QtCore/qdiriterator.h>
+#include <QtCore/qjsonobject.h>
+#include <QtCore/qjsonarray.h>
+#include <QtCore/qjsondocument.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/QMutexLocker>
+#include <QtCore/qscopedpointer.h>
+#include <QtCore/qrunnable.h>
+#include <QtCore/qthreadpool.h>
+#include <QtCore/qtimer.h>
+
+#include <QtJsonRpc/private/qhttpmessagestreamparser_p.h>
+
+#include <QtQmlCompiler/private/qqmljsresourcefilemapper_p.h>
+#include <QtQmlCompiler/private/qqmljscompiler_p.h>
+#include <QtQmlCompiler/private/qqmljslogger_p.h>
+#include <QtQmlCompiler/private/qqmljsscope_p.h>
+#include <QtQmlCompiler/private/qqmljsimporter_p.h>
+#if QT_CONFIG(commandlineparser)
+# include <QtCore/qcommandlineparser.h>
+#endif
+
+#ifndef QT_BOOTSTRAPPED
+# include <QtCore/qlibraryinfo.h>
+#endif
+
+#include <iostream>
+#ifdef Q_OS_WIN32
+# include <fcntl.h>
+# include <io.h>
+#endif
+
+using namespace QmlLsp;
+
+QFile *logFile = nullptr;
+QBasicMutex *logFileLock = nullptr;
+
+class StdinReader : public QObject
+{
+ Q_OBJECT
+public:
+ void run()
+ {
+ auto guard = qScopeGuard([this]() { emit eof(); });
+ const constexpr qsizetype bufSize = 1024;
+ qsizetype bytesInBuf = 0;
+ char bufferData[2 * bufSize];
+ char *buffer = static_cast<char *>(bufferData);
+
+ auto trySend = [this, &bytesInBuf, buffer]() {
+ if (bytesInBuf == 0)
+ return;
+ qsizetype toSend = bytesInBuf;
+ bytesInBuf = 0;
+ QByteArray dataToSend(buffer, toSend);
+ emit receivedData(dataToSend);
+ };
+ QHttpMessageStreamParser streamParser(
+ [](const QByteArray &, const QByteArray &) { /* just a header, do nothing */ },
+ [&trySend](const QByteArray &) {
+ // message body
+ trySend();
+ },
+ [&trySend](QtMsgType, QString) {
+ // there was an error
+ trySend();
+ },
+ QHttpMessageStreamParser::UNBUFFERED);
+
+ while (std::cin.get(buffer[bytesInBuf])) { // should poll/select and process events
+ qsizetype readNow = std::cin.readsome(buffer + bytesInBuf + 1, bufSize) + 1;
+ QByteArray toAdd(buffer + bytesInBuf, readNow);
+ bytesInBuf += readNow;
+ if (bytesInBuf >= bufSize)
+ trySend();
+ streamParser.receiveData(toAdd);
+ }
+ trySend();
+ }
+signals:
+ void receivedData(const QByteArray &data);
+ void eof();
+};
+
+// To debug:
+//
+// * simple logging can be redirected to a file
+// passing -l <file> to the qmlls command
+//
+// * more complex debugging can use named pipes:
+//
+// mkfifo qmllsIn
+// mkfifo qmllsOut
+//
+// this together with a qmllsEcho script that can be defined as
+//
+// #!/bin/sh
+// cat -u < ~/qmllsOut &
+// cat -u > ~/qmllsIn
+//
+// allows to use qmllsEcho as lsp server, and still easily start
+// it in a terminal
+//
+// qmlls < ~/qmllsIn > ~/qmllsOut
+//
+// * statup can be slowed down to have the time to attach via the
+// -w <nSeconds> flag.
+
+int main(int argv, char *argc[])
+{
+#ifdef Q_OS_WIN32
+ // windows does not open stdin/stdout in binary mode by default
+ int err = _setmode(_fileno(stdout), _O_BINARY);
+ if (err == -1)
+ perror("Cannot set mode for stdout");
+ err = _setmode(_fileno(stdin), _O_BINARY);
+ if (err == -1)
+ perror("Cannot set mode for stdin");
+#endif
+
+ QHashSeed::setDeterministicGlobalSeed();
+ QCoreApplication app(argv, argc);
+ QCoreApplication::setApplicationName("qmlls");
+ QCoreApplication::setApplicationVersion(QT_VERSION_STR);
+
+ QCommandLineParser parser;
+ QQmlToolingSettings settings(QLatin1String("qmlls"));
+ parser.setApplicationDescription(QLatin1String(R"(QML languageserver)"));
+
+ parser.addHelpOption();
+ QCommandLineOption waitOption(QStringList() << "w"
+ << "wait",
+ QLatin1String("Waits the given number of seconds before startup"),
+ QLatin1String("waitSeconds"));
+ parser.addOption(waitOption);
+
+ QCommandLineOption verboseOption(
+ QStringList() << "v"
+ << "verbose",
+ QLatin1String("Outputs extra information on the operations being performed"));
+ parser.addOption(verboseOption);
+
+ QCommandLineOption logFileOption(QStringList() << "l"
+ << "log-file",
+ QLatin1String("Writes logging to the given file"),
+ QLatin1String("logFile"));
+ parser.addOption(logFileOption);
+
+ QString buildDir = QStringLiteral(u"buildDir");
+ QCommandLineOption buildDirOption(
+ QStringList() << "b"
+ << "build-dir",
+ QLatin1String("Adds a build dir to look up for qml information"), buildDir);
+ parser.addOption(buildDirOption);
+ settings.addOption(buildDir);
+
+ QString qmlImportPath = QStringLiteral(u"qml-import-path");
+ QCommandLineOption qmlImportPathOption(
+ QStringList() << "I", QLatin1String("Look for QML modules in the specified directory"),
+ qmlImportPath);
+ parser.addOption(qmlImportPathOption);
+
+ QCommandLineOption environmentOption(
+ QStringList() << "E",
+ QLatin1String("Use the QML_IMPORT_PATH environment variable to look for QML Modules"));
+ parser.addOption(environmentOption);
+
+ QCommandLineOption writeDefaultsOption(
+ QStringList() << "write-defaults",
+ QLatin1String("Writes defaults settings to .qmlls.ini and exits (Warning: This "
+ "will overwrite any existing settings and comments!)"));
+ parser.addOption(writeDefaultsOption);
+
+ QCommandLineOption ignoreSettings(QStringList() << "ignore-settings",
+ QLatin1String("Ignores all settings files and only takes "
+ "command line options into consideration"));
+ parser.addOption(ignoreSettings);
+
+ QCommandLineOption noCMakeCallsOption(
+ QStringList() << "no-cmake-calls",
+ QLatin1String("Disables automatic CMake rebuilds and C++ file watching."));
+ parser.addOption(noCMakeCallsOption);
+ settings.addOption("no-cmake-calls", "false");
+
+ parser.process(app);
+
+ if (parser.isSet(writeDefaultsOption)) {
+ return settings.writeDefaults() ? 0 : 1;
+ }
+ if (parser.isSet(logFileOption)) {
+ QString fileName = parser.value(logFileOption);
+ qInfo() << "will log to" << fileName;
+ logFile = new QFile(fileName);
+ logFileLock = new QMutex;
+ logFile->open(QFile::WriteOnly | QFile::Truncate | QFile::Text);
+ qInstallMessageHandler([](QtMsgType t, const QMessageLogContext &, const QString &msg) {
+ QMutexLocker l(logFileLock);
+ logFile->write(QString::number(int(t)).toUtf8());
+ logFile->write(" ");
+ logFile->write(msg.toUtf8());
+ logFile->write("\n");
+ logFile->flush();
+ });
+ }
+ if (parser.isSet(verboseOption))
+ QLoggingCategory::setFilterRules("qt.languageserver*.debug=true\n");
+ if (parser.isSet(waitOption)) {
+ int waitSeconds = parser.value(waitOption).toInt();
+ if (waitSeconds > 0)
+ qDebug() << "waiting";
+ QThread::sleep(waitSeconds);
+ qDebug() << "starting";
+ }
+ QMutex writeMutex;
+ QQmlLanguageServer qmlServer(
+ [&writeMutex](const QByteArray &data) {
+ QMutexLocker l(&writeMutex);
+ std::cout.write(data.constData(), data.size());
+ std::cout.flush();
+ },
+ (parser.isSet(ignoreSettings) ? nullptr : &settings));
+
+ const bool disableCMakeCallsViaEnvironment =
+ qmlGetConfigOption<bool, qmlConvertBoolConfigOption>("QMLLS_NO_CMAKE_CALLS");
+
+ if (disableCMakeCallsViaEnvironment || parser.isSet(noCMakeCallsOption)) {
+ if (disableCMakeCallsViaEnvironment) {
+ qWarning() << "Disabling CMake calls via QMLLS_NO_CMAKE_CALLS environment variable.";
+ } else {
+ qWarning() << "Disabling CMake calls via command line switch.";
+ }
+
+ qmlServer.codeModel()->disableCMakeCalls();
+ }
+
+ if (parser.isSet(buildDirOption)) {
+ const QStringList dirs =
+ QQmlToolingUtils::getAndWarnForInvalidDirsFromOption(parser, buildDirOption);
+
+ qInfo().nospace().noquote()
+ << "Using build directories passed by -b: \"" << dirs.join(u"\", \""_s) << "\".";
+
+ qmlServer.codeModel()->setBuildPathsForRootUrl(QByteArray(), dirs);
+ } else if (QStringList dirsFromEnv =
+ QQmlToolingUtils::getAndWarnForInvalidDirsFromEnv("QMLLS_BUILD_DIRS");
+ !dirsFromEnv.isEmpty()) {
+
+ // warn now at qmlls startup that those directories will be used later in qqmlcodemodel when
+ // searching for build folders.
+ qInfo().nospace().noquote() << "Using build directories passed from environment variable "
+ "\"QMLLS_BUILD_DIRS\": \""
+ << dirsFromEnv.join(u"\", \""_s) << "\".";
+
+ } else {
+ qInfo() << "Using the build directories found in the .qmlls.ini file. Your build folder "
+ "might not be found if no .qmlls.ini files are present in the root source "
+ "folder.";
+ }
+ QStringList importPaths{ QLibraryInfo::path(QLibraryInfo::QmlImportsPath) };
+ if (parser.isSet(qmlImportPathOption)) {
+ const QStringList pathsFromOption =
+ QQmlToolingUtils::getAndWarnForInvalidDirsFromOption(parser, qmlImportPathOption);
+ qInfo().nospace().noquote() << "Using import directories passed by -I: \""
+ << pathsFromOption.join(u"\", \""_s) << "\".";
+ importPaths << pathsFromOption;
+ }
+ if (parser.isSet(environmentOption)) {
+ if (const QStringList dirsFromEnv =
+ QQmlToolingUtils::getAndWarnForInvalidDirsFromEnv(u"QML_IMPORT_PATH"_s);
+ !dirsFromEnv.isEmpty()) {
+ qInfo().nospace().noquote()
+ << "Using import directories passed from environment variable "
+ "\"QML_IMPORT_PATH\": \""
+ << dirsFromEnv.join(u"\", \""_s) << "\".";
+ importPaths << dirsFromEnv;
+ }
+
+ if (const QStringList dirsFromEnv2 =
+ QQmlToolingUtils::getAndWarnForInvalidDirsFromEnv(u"QML2_IMPORT_PATH"_s);
+ !dirsFromEnv2.isEmpty()) {
+ qInfo().nospace().noquote()
+ << "Using import directories passed from the deprecated environment variable "
+ "\"QML2_IMPORT_PATH\": \""
+ << dirsFromEnv2.join(u"\", \""_s) << "\".";
+ importPaths << dirsFromEnv2;
+ }
+ }
+ qmlServer.codeModel()->setImportPaths(importPaths);
+
+ StdinReader r;
+ QObject::connect(&r, &StdinReader::receivedData,
+ qmlServer.server(), &QLanguageServer::receiveData);
+ QObject::connect(&r, &StdinReader::eof, &app, [&app]() {
+ QTimer::singleShot(100, &app, []() {
+ QCoreApplication::processEvents();
+ QCoreApplication::exit();
+ });
+ });
+ QThreadPool::globalInstance()->start([&r]() { r.run(); });
+ app.exec();
+ return qmlServer.returnValue();
+}
+
+#include "qmllanguageservertool.moc"
diff --git a/tools/qmlplugindump/.prev_CMakeLists.txt b/tools/qmlplugindump/.prev_CMakeLists.txt
deleted file mode 100644
index c4747ea3ab..0000000000
--- a/tools/qmlplugindump/.prev_CMakeLists.txt
+++ /dev/null
@@ -1,39 +0,0 @@
-# Generated from qmlplugindump.pro.
-
-#####################################################################
-## qmlplugindump Tool:
-#####################################################################
-
-qt_get_tool_target_name(target_name qmlplugindump)
-qt_internal_add_tool(${target_name}
- TARGET_DESCRIPTION "QML Plugin Metadata Dumper"
- SOURCES
- ../../src/qmlcompiler/qqmljsstreamwriter.cpp ../../src/qmlcompiler/qqmljsstreamwriter_p.h
- main.cpp
- qmltypereader.cpp qmltypereader.h
- INCLUDE_DIRECTORIES
- ../../src/qmlcompiler
- PUBLIC_LIBRARIES
- Qt::CorePrivate
- Qt::Gui
- Qt::Qml
- Qt::QmlPrivate
- Qt::QuickPrivate
-)
-
-#### Keys ignored in scope 1:.:.:qmlplugindump.pro:<TRUE>:
-# QMAKE_TARGET_DESCRIPTION = "QML" "Plugin" "Metadata" "Dumper"
-# QTPLUGIN.platforms = "qminimal"
-
-## Scopes:
-#####################################################################
-
-qt_internal_extend_target(${target_name} CONDITION TARGET Qt::Widgets
- PUBLIC_LIBRARIES
- Qt::Widgets
-)
-
-qt_internal_extend_target(${target_name} CONDITION MACOS
- LINK_OPTIONS
- "-Wl,-sectcreate,__TEXT,__info_plist,$$shell_quote$$PWD/Info.plist"
-)
diff --git a/tools/qmlplugindump/CMakeLists.txt b/tools/qmlplugindump/CMakeLists.txt
index c36d743611..c2788f03a0 100644
--- a/tools/qmlplugindump/CMakeLists.txt
+++ b/tools/qmlplugindump/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qmlplugindump.pro.
#####################################################################
@@ -9,18 +12,17 @@ qt_internal_add_tool(${target_name}
TARGET_DESCRIPTION "QML Plugin Metadata Dumper"
TOOLS_TARGET Qml # special case
SOURCES
- ../../src/qmlcompiler/qqmljsstreamwriter.cpp ../../src/qmlcompiler/qqmljsstreamwriter_p.h
main.cpp
qmltypereader.cpp qmltypereader.h
- INCLUDE_DIRECTORIES
- ../../src/qmlcompiler
- PUBLIC_LIBRARIES
+ LIBRARIES
+ Qt::QmlTypeRegistrarPrivate
Qt::CorePrivate
Qt::Gui
Qt::Qml
Qt::QmlPrivate
Qt::QuickPrivate
)
+qt_internal_return_unless_building_tools()
#### Keys ignored in scope 1:.:.:qmlplugindump.pro:<TRUE>:
# QMAKE_TARGET_DESCRIPTION = "QML" "Plugin" "Metadata" "Dumper"
diff --git a/tools/qmlplugindump/main.cpp b/tools/qmlplugindump/main.cpp
index 9ee6db39da..ead1e729b9 100644
--- a/tools/qmlplugindump/main.cpp
+++ b/tools/qmlplugindump/main.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QtQml/qqmlengine.h>
#include <QtQml/private/qqmlengine_p.h>
@@ -54,13 +29,14 @@
#include <QtCore/QProcess>
#include <QtCore/private/qobject_p.h>
#include <QtCore/private/qmetaobject_p.h>
+#include <QtQmlTypeRegistrar/private/qqmljsstreamwriter_p.h>
+#include <QtQml/private/qqmlsignalnames_p.h>
#include <QRegularExpression>
#include <iostream>
#include <algorithm>
#include "qmltypereader.h"
-#include "qqmljsstreamwriter_p.h"
#ifdef QT_SIMULATOR
#include <QtGui/private/qsimulatorconnection_p.h>
@@ -93,13 +69,6 @@ QString inObjectInstantiation;
}
-static QString enquote(const QString &string)
-{
- QString s = string;
- return QString("\"%1\"").arg(s.replace(QLatin1Char('\\'), QLatin1String("\\\\"))
- .replace(QLatin1Char('"'),QLatin1String("\\\"")));
-}
-
struct QmlVersionInfo
{
QString pluginImportUri;
@@ -261,7 +230,8 @@ QSet<const QMetaObject *> collectReachableMetaObjects(QQmlEngine *engine,
if (ty.isSingleton())
singletons.insert(ty.baseMetaObject());
if (!ty.isComposite()) {
- qmlTypesByCppName[ty.baseMetaObject()->className()].insert(ty);
+ if (const QMetaObject *mo = ty.baseMetaObject())
+ qmlTypesByCppName[mo->className()].insert(ty);
collectReachableMetaObjects(QQmlEnginePrivate::get(engine), ty, &metas, info);
} else {
compositeTypes[ty.elementName()].append(ty);
@@ -292,7 +262,7 @@ QSet<const QMetaObject *> collectReachableMetaObjects(QQmlEngine *engine,
QObject *object = nullptr;
if (ty.isSingleton()) {
- QQmlType::SingletonInstanceInfo *siinfo = ty.singletonInstanceInfo();
+ QQmlType::SingletonInstanceInfo::ConstPtr siinfo = ty.singletonInstanceInfo();
if (!siinfo) {
std::cerr << "Internal error, " << qPrintable(tyName)
<< "(" << qPrintable( QString::fromUtf8(ty.typeName()) ) << ")"
@@ -302,7 +272,8 @@ QSet<const QMetaObject *> collectReachableMetaObjects(QQmlEngine *engine,
if (ty.isQObjectSingleton()) {
if (verbose)
std::cerr << "Trying to get singleton for " << qPrintable(tyName)
- << " (" << qPrintable( siinfo->typeName ) << ")" << std::endl;
+ << " (" << qPrintable( QString::fromUtf8(siinfo->typeName) )
+ << ")" << std::endl;
collectReachableMetaObjects(object, &metas, info);
object = QQmlEnginePrivate::get(engine)->singletonInstance<QObject*>(ty);
} else {
@@ -372,7 +343,7 @@ public:
relocatableModuleUri = uri;
}
- QString getExportString(const QQmlType &type, const QmlVersionInfo &versionInfo)
+ QByteArray getExportString(const QQmlType &type, const QmlVersionInfo &versionInfo)
{
const QString module = type.module().isEmpty() ? versionInfo.pluginImportUri
: type.module();
@@ -382,12 +353,14 @@ public:
type.version().hasMinorVersion() ? type.version().minorVersion()
: versionInfo.version.minorVersion());
- const QString versionedElement = type.elementName()
- + QString::fromLatin1(" %1.%2").arg(version.majorVersion()).arg(version.minorVersion());
+ const QByteArray versionedElement
+ = (type.elementName() + QString::fromLatin1(" %1.%2")
+ .arg(version.majorVersion())
+ .arg(version.minorVersion())).toUtf8();
- return enquote((module == relocatableModuleUri)
+ return (module == relocatableModuleUri)
? versionedElement
- : module + QLatin1Char('/') + versionedElement);
+ : module.toUtf8() + '/' + versionedElement;
}
void writeMetaContent(const QMetaObject *meta, KnownAttributes *knownAttributes = nullptr)
@@ -399,30 +372,31 @@ public:
for (int index = meta->methodOffset(); index < meta->methodCount(); ++index) {
QMetaMethod method = meta->method(index);
QByteArray signature = method.methodSignature();
- if (signature == QByteArrayLiteral("destroyed(QObject*)")
- || signature == QByteArrayLiteral("destroyed()")
- || signature == QByteArrayLiteral("deleteLater()"))
+ if (signature == "destroyed(QObject*)"
+ || signature == "destroyed()"
+ || signature == "deleteLater()") {
continue;
+ }
dump(method, implicitSignals, knownAttributes);
}
// and add toString(), destroy() and destroy(int)
- if (!knownAttributes || !knownAttributes->knownMethod(QByteArray("toString"), 0, QTypeRevision::zero())) {
- qml->writeStartObject(QLatin1String("Method"));
- qml->writeScriptBinding(QLatin1String("name"), enquote(QLatin1String("toString")));
+ if (!knownAttributes || !knownAttributes->knownMethod("toString", 0, QTypeRevision::zero())) {
+ qml->writeStartObject("Method");
+ qml->writeStringBinding("name", QLatin1String("toString"));
qml->writeEndObject();
}
- if (!knownAttributes || !knownAttributes->knownMethod(QByteArray("destroy"), 0, QTypeRevision::zero())) {
- qml->writeStartObject(QLatin1String("Method"));
- qml->writeScriptBinding(QLatin1String("name"), enquote(QLatin1String("destroy")));
+ if (!knownAttributes || !knownAttributes->knownMethod("destroy", 0, QTypeRevision::zero())) {
+ qml->writeStartObject("Method");
+ qml->writeStringBinding("name", QLatin1String("destroy"));
qml->writeEndObject();
}
- if (!knownAttributes || !knownAttributes->knownMethod(QByteArray("destroy"), 1, QTypeRevision::zero())) {
- qml->writeStartObject(QLatin1String("Method"));
- qml->writeScriptBinding(QLatin1String("name"), enquote(QLatin1String("destroy")));
- qml->writeStartObject(QLatin1String("Parameter"));
- qml->writeScriptBinding(QLatin1String("name"), enquote(QLatin1String("delay")));
- qml->writeScriptBinding(QLatin1String("type"), enquote(QLatin1String("int")));
+ if (!knownAttributes || !knownAttributes->knownMethod("destroy", 1, QTypeRevision::zero())) {
+ qml->writeStartObject("Method");
+ qml->writeStringBinding("name", QLatin1String("destroy"));
+ qml->writeStartObject("Parameter");
+ qml->writeStringBinding("name", QLatin1String("delay"));
+ qml->writeStringBinding("type", QLatin1String("int"));
qml->writeEndObject();
qml->writeEndObject();
}
@@ -432,12 +406,12 @@ public:
}
}
- QString getPrototypeNameForCompositeType(
+ QByteArray getPrototypeNameForCompositeType(
const QMetaObject *metaObject, QList<const QMetaObject *> *objectsToMerge,
const QmlVersionInfo &versionInfo)
{
auto ty = QQmlMetaType::qmlType(metaObject);
- QString prototypeName;
+ QByteArray prototypeName;
if (matchingImportUri(ty, versionInfo)) {
// dynamic meta objects can break things badly
// but extended types are usually fine
@@ -488,40 +462,41 @@ public:
QList<const QMetaObject *> objectsToMerge;
KnownAttributes knownAttributes;
// Get C++ base class name for the composite type
- QString prototypeName = getPrototypeNameForCompositeType(mainMeta, &objectsToMerge,
- versionInfo);
- qml->writeScriptBinding(QLatin1String("prototype"), enquote(prototypeName));
+ QByteArray prototypeName = getPrototypeNameForCompositeType(
+ mainMeta, &objectsToMerge, versionInfo);
+ qml->writeStringBinding("prototype", QUtf8StringView(prototypeName));
- QString qmlTyName = compositeType.qmlTypeName();
- const QString exportString = getExportString(compositeType, versionInfo);
+ const QByteArray exportString = getExportString(compositeType, versionInfo);
// TODO: why don't we simply output the compositeType.elementName() here?
// That would make more sense, but it would change the format quite a bit.
- qml->writeScriptBinding(QLatin1String("name"), exportString);
+ qml->writeStringBinding("name", QUtf8StringView(exportString));
- qml->writeArrayBinding(QLatin1String("exports"), QStringList() << exportString);
+ qml->writeStringListBinding(
+ "exports", QList<QAnyStringView> { QUtf8StringView(exportString) });
// TODO: shouldn't this be metaObjectRevision().value<quint16>()
// rather than version().minorVersion()
- qml->writeArrayBinding(QLatin1String("exportMetaObjectRevisions"), QStringList()
- << QString::number(compositeType.version().minorVersion()));
+ qml->writeArrayBinding(
+ "exportMetaObjectRevisions",
+ QByteArrayList() << QByteArray::number(compositeType.version().minorVersion()));
- qml->writeBooleanBinding(QLatin1String("isComposite"), true);
+ qml->writeBooleanBinding("isComposite", true);
if (compositeType.isSingleton()) {
- qml->writeBooleanBinding(QLatin1String("isCreatable"), false);
- qml->writeBooleanBinding(QLatin1String("isSingleton"), true);
+ qml->writeBooleanBinding("isCreatable", false);
+ qml->writeBooleanBinding("isSingleton", true);
}
for (int index = mainMeta->classInfoCount() - 1 ; index >= 0 ; --index) {
QMetaClassInfo classInfo = mainMeta->classInfo(index);
- if (QLatin1String(classInfo.name()) == QLatin1String("DefaultProperty")) {
- qml->writeScriptBinding(QLatin1String("defaultProperty"), enquote(QLatin1String(classInfo.value())));
+ if (QUtf8StringView(classInfo.name()) == QUtf8StringView("DefaultProperty")) {
+ qml->writeStringBinding("defaultProperty", QUtf8StringView(classInfo.value()));
break;
}
}
- for (const QMetaObject *meta : qAsConst(objectsToMerge)) {
+ for (const QMetaObject *meta : std::as_const(objectsToMerge)) {
for (int index = meta->enumeratorOffset(); index < meta->enumeratorCount(); ++index)
dump(meta->enumerator(index));
@@ -531,22 +506,29 @@ public:
qml->writeEndObject();
}
- QString getDefaultProperty(const QMetaObject *meta)
+ QByteArray getDefaultProperty(const QMetaObject *meta)
{
for (int index = meta->classInfoCount() - 1; index >= 0; --index) {
QMetaClassInfo classInfo = meta->classInfo(index);
if (QLatin1String(classInfo.name()) == QLatin1String("DefaultProperty")) {
- return QLatin1String(classInfo.value());
+ return QByteArray(classInfo.value());
}
}
- return QString();
+ return QByteArray();
}
struct QmlTypeInfo {
QmlTypeInfo() {}
- QmlTypeInfo(const QString &exportString, QTypeRevision revision, const QMetaObject *extendedObject, QByteArray attachedTypeId)
- : exportString(exportString), revision(revision), extendedObject(extendedObject), attachedTypeId(attachedTypeId) {}
- QString exportString;
+ QmlTypeInfo(
+ const QByteArray &exportString, QTypeRevision revision,
+ const QMetaObject *extendedObject, QByteArray attachedTypeId)
+ : exportString(exportString)
+ , revision(revision)
+ , extendedObject(extendedObject)
+ , attachedTypeId(attachedTypeId)
+ {}
+
+ QByteArray exportString;
QTypeRevision revision = QTypeRevision::zero();
const QMetaObject *extendedObject = nullptr;
QByteArray attachedTypeId;
@@ -557,11 +539,12 @@ public:
qml->writeStartObject("Component");
QByteArray id = convertToId(meta);
- qml->writeScriptBinding(QLatin1String("name"), enquote(id));
+ qml->writeStringBinding("name", QUtf8StringView(id));
// collect type information
QVector<QmlTypeInfo> typeInfo;
- for (QQmlType type : qmlTypesByCppName.value(meta->className())) {
+ const auto types = qmlTypesByCppName.value(meta->className());
+ for (const QQmlType &type : types) {
const QMetaObject *extendedObject = type.extensionFunction() ? type.metaObject() : nullptr;
QByteArray attachedTypeId;
if (const QMetaObject *attachedType = type.attachedPropertiesType(engine)) {
@@ -570,7 +553,8 @@ public:
if (attachedType != meta)
attachedTypeId = convertToId(attachedType);
}
- const QString exportString = getExportString(type, { QString(), QTypeRevision(), false });
+ const QByteArray exportString = getExportString(
+ type, { QString(), QTypeRevision(), false });
QTypeRevision metaObjectRevision = type.metaObjectRevision();
if (extendedObject) {
// emulate custom metaobjectrevision out of import
@@ -588,7 +572,7 @@ public:
// determine default property
// TODO: support revisioning of default property
- QString defaultProperty = getDefaultProperty(meta);
+ QByteArray defaultProperty = getDefaultProperty(meta);
if (defaultProperty.isEmpty()) {
for (const QmlTypeInfo &iter : typeInfo) {
if (iter.extendedObject) {
@@ -599,31 +583,33 @@ public:
}
}
if (!defaultProperty.isEmpty())
- qml->writeScriptBinding(QLatin1String("defaultProperty"), enquote(defaultProperty));
+ qml->writeStringBinding("defaultProperty", defaultProperty);
if (meta->superClass())
- qml->writeScriptBinding(QLatin1String("prototype"), enquote(convertToId(meta->superClass())));
+ qml->writeStringBinding("prototype", convertToId(meta->superClass()));
if (!typeInfo.isEmpty()) {
- QMap<QString, QString> exports; // sort exports
- for (const QmlTypeInfo &iter : typeInfo)
- exports.insert(iter.exportString, QString::number(iter.revision.toEncodedVersion<quint16>()));
+ QMap<QAnyStringView, QByteArray> exports; // sort exports
+ for (const QmlTypeInfo &iter : typeInfo) {
+ exports.insert(
+ QUtf8StringView(iter.exportString),
+ QByteArray::number(iter.revision.toEncodedVersion<quint16>()));
+ }
- QStringList exportStrings = exports.keys();
- QStringList metaObjectRevisions = exports.values();
- qml->writeArrayBinding(QLatin1String("exports"), exportStrings);
+ QByteArrayList metaObjectRevisions = exports.values();
+ qml->writeStringListBinding("exports", exports.keys());
if (isUncreatable)
- qml->writeBooleanBinding(QLatin1String("isCreatable"), false);
+ qml->writeBooleanBinding("isCreatable", false);
if (isSingleton)
- qml->writeBooleanBinding(QLatin1String("isSingleton"), true);
+ qml->writeBooleanBinding("isSingleton", true);
- qml->writeArrayBinding(QLatin1String("exportMetaObjectRevisions"), metaObjectRevisions);
+ qml->writeArrayBinding("exportMetaObjectRevisions", metaObjectRevisions);
for (const QmlTypeInfo &iter : typeInfo) {
if (!iter.attachedTypeId.isEmpty()) {
- qml->writeScriptBinding(QLatin1String("attachedType"), enquote(iter.attachedTypeId));
+ qml->writeStringBinding("attachedType", iter.attachedTypeId);
break;
}
}
@@ -635,7 +621,7 @@ public:
writeMetaContent(meta);
// dump properties from extended metaobjects last
- for (auto iter : typeInfo) {
+ for (const auto &iter : typeInfo) {
if (iter.extendedObject)
dumpMetaProperties(iter.extendedObject, iter.revision);
}
@@ -654,11 +640,11 @@ private:
if (typeName->endsWith('*')) {
*isPointer = true;
- typeName->truncate(typeName->length() - 1);
+ typeName->truncate(typeName->size() - 1);
removePointerAndList(typeName, isList, isPointer);
} else if (typeName->startsWith(declListPrefix)) {
*isList = true;
- typeName->truncate(typeName->length() - 1); // get rid of the suffix '>'
+ typeName->truncate(typeName->size() - 1); // get rid of the suffix '>'
*typeName = typeName->mid(declListPrefix.size());
removePointerAndList(typeName, isList, isPointer);
}
@@ -671,13 +657,13 @@ private:
bool isList = false, isPointer = false;
removePointerAndList(&typeName, &isList, &isPointer);
- qml->writeScriptBinding(QLatin1String("type"), enquote(typeName));
+ qml->writeStringBinding("type", QUtf8StringView(typeName));
if (isList)
- qml->writeScriptBinding(QLatin1String("isList"), QLatin1String("true"));
+ qml->writeBooleanBinding("isList", true);
if (!isWritable)
- qml->writeScriptBinding(QLatin1String("isReadonly"), QLatin1String("true"));
+ qml->writeBooleanBinding("isReadonly", true);
if (isPointer)
- qml->writeScriptBinding(QLatin1String("isPointer"), QLatin1String("true"));
+ qml->writeBooleanBinding("isPointer", true);
}
void dump(const QMetaProperty &prop, QTypeRevision metaRevision = QTypeRevision(),
@@ -691,9 +677,9 @@ private:
if (knownAttributes && knownAttributes->knownProperty(propName, revision))
return;
qml->writeStartObject("Property");
- qml->writeScriptBinding(QLatin1String("name"), enquote(QString::fromUtf8(prop.name())));
+ qml->writeStringBinding("name", QUtf8StringView(prop.name()));
if (revision != QTypeRevision::zero())
- qml->writeScriptBinding(QLatin1String("revision"), QString::number(revision.toEncodedVersion<quint16>()));
+ qml->writeNumberBinding("revision", revision.toEncodedVersion<quint16>());
writeTypeProperties(prop.typeName(), prop.isWritable());
qml->writeEndObject();
@@ -706,10 +692,13 @@ private:
for (int index = meta->propertyOffset(); index < meta->propertyCount(); ++index) {
const QMetaProperty &property = meta->property(index);
dump(property, metaRevision, knownAttributes);
+ const QByteArray changedSignalName =
+ QQmlSignalNames::propertyNameToChangedSignalName(property.name());
if (knownAttributes)
- knownAttributes->knownMethod(QByteArray(property.name()).append("Changed"),
- 0, QTypeRevision::fromEncodedVersion(property.revision()));
- implicitSignals.insert(QString("%1Changed").arg(QString::fromUtf8(property.name())));
+ knownAttributes->knownMethod(
+ changedSignalName, 0,
+ QTypeRevision::fromEncodedVersion(property.revision()));
+ implicitSignals.insert(changedSignalName);
}
return implicitSignals;
}
@@ -725,13 +714,13 @@ private:
}
QByteArray name = meth.name();
- const QString typeName = convertToId(meth.typeName());
+ const QByteArray typeName = convertToId(meth.typeName());
if (implicitSignals.contains(name)
&& !meth.revision()
&& meth.methodType() == QMetaMethod::Signal
&& meth.parameterNames().isEmpty()
- && typeName == QLatin1String("void")) {
+ && typeName == "void") {
// don't mention implicit signals
return;
}
@@ -740,24 +729,24 @@ private:
if (knownAttributes && knownAttributes->knownMethod(name, meth.parameterNames().size(), revision))
return;
if (meth.methodType() == QMetaMethod::Signal)
- qml->writeStartObject(QLatin1String("Signal"));
+ qml->writeStartObject("Signal");
else
- qml->writeStartObject(QLatin1String("Method"));
+ qml->writeStartObject("Method");
- qml->writeScriptBinding(QLatin1String("name"), enquote(name));
+ qml->writeStringBinding("name", QUtf8StringView(name));
if (revision != QTypeRevision::zero())
- qml->writeScriptBinding(QLatin1String("revision"), QString::number(revision.toEncodedVersion<quint16>()));
+ qml->writeNumberBinding("revision", revision.toEncodedVersion<quint16>());
- if (typeName != QLatin1String("void"))
- qml->writeScriptBinding(QLatin1String("type"), enquote(typeName));
+ if (typeName != "void")
+ qml->writeStringBinding("type", QUtf8StringView(typeName));
for (int i = 0; i < meth.parameterTypes().size(); ++i) {
QByteArray argName = meth.parameterNames().at(i);
- qml->writeStartObject(QLatin1String("Parameter"));
- if (! argName.isEmpty())
- qml->writeScriptBinding(QLatin1String("name"), enquote(argName));
+ qml->writeStartObject("Parameter");
+ if (!argName.isEmpty())
+ qml->writeStringBinding("name", QUtf8StringView(argName));
writeTypeProperties(meth.parameterTypes().at(i), true);
qml->writeEndObject();
}
@@ -767,17 +756,16 @@ private:
void dump(const QMetaEnum &e)
{
- qml->writeStartObject(QLatin1String("Enum"));
- qml->writeScriptBinding(QLatin1String("name"), enquote(QString::fromUtf8(e.name())));
+ qml->writeStartObject("Enum");
+ qml->writeStringBinding("name", QUtf8StringView(e.name()));
- QList<QPair<QString, QString> > namesValues;
+ QList<QPair<QAnyStringView, int>> namesValues;
const int keyCount = e.keyCount();
namesValues.reserve(keyCount);
- for (int index = 0; index < keyCount; ++index) {
- namesValues.append(qMakePair(enquote(QString::fromUtf8(e.key(index))), QString::number(e.value(index))));
- }
+ for (int index = 0; index < keyCount; ++index)
+ namesValues.append(qMakePair(QUtf8StringView(e.key(index)), e.value(index)));
- qml->writeScriptObjectLiteralBinding(QLatin1String("values"), namesValues);
+ qml->writeEnumObjectLiteralBinding("values", namesValues);
qml->writeEndObject();
}
};
@@ -835,7 +823,7 @@ static bool readDependenciesData(QString dependenciesFile, const QByteArray &fil
continue;
if (name.contains(QLatin1String("Private"), Qt::CaseInsensitive)) {
if (verbose)
- std::cerr << "skipping private dependecy "
+ std::cerr << "skipping private dependency "
<< qPrintable( name ) << " " << qPrintable(version) << std::endl;
continue;
}
@@ -910,7 +898,7 @@ static bool getDependencies(const QQmlEngine &engine, const QString &pluginImpor
if (!importScanner.waitForFinished()) {
std::cerr << "failure to start " << qPrintable(command);
- for (const QString &arg : qAsConst(commandArgs))
+ for (const QString &arg : std::as_const(commandArgs))
std::cerr << ' ' << qPrintable(arg);
std::cerr << std::endl;
return false;
@@ -945,18 +933,18 @@ bool dependencyBetter(const QString &lhs, const QString &rhs)
if (leftModule > rightModule)
return false;
- if (leftSegments.length() == 1)
+ if (leftSegments.size() == 1)
return false;
- if (rightSegments.length() == 1)
+ if (rightSegments.size() == 1)
return true;
const QStringList leftVersion = leftSegments.at(1).split(QLatin1Char('.'));
const QStringList rightVersion = rightSegments.at(1).split(QLatin1Char('.'));
auto compareSegment = [&](int segmentIndex) {
- if (leftVersion.length() <= segmentIndex)
- return rightVersion.length() > segmentIndex ? 1 : 0;
- if (rightVersion.length() <= segmentIndex)
+ if (leftVersion.size() <= segmentIndex)
+ return rightVersion.size() > segmentIndex ? 1 : 0;
+ if (rightVersion.size() <= segmentIndex)
return -1;
bool leftOk = false;
@@ -991,19 +979,9 @@ void compactDependencies(QStringList *dependencies)
}
}
-inline std::wostream &operator<<(std::wostream &str, const QString &s)
-{
-#ifdef Q_OS_WIN
- str << reinterpret_cast<const wchar_t *>(s.utf16());
-#else
- str << s.toStdWString();
-#endif
- return str;
-}
-
void printDebugMessage(QtMsgType, const QMessageLogContext &, const QString &msg)
{
- std::wcerr << msg << std::endl;
+ std::cerr << msg.toStdString() << std::endl;
// In case of QtFatalMsg the calling code will abort() when appropriate.
}
@@ -1192,23 +1170,26 @@ int main(int argc, char *argv[])
QDir cur = QDir::current();
cur.cd(pluginImportPath);
pluginImportPath = cur.canonicalPath();
- QDir::setCurrent(pluginImportPath);
+ if (!QDir::setCurrent(pluginImportPath)) {
+ std::cerr << "Cannot set current directory to import path "
+ << qPrintable(pluginImportPath) << std::endl;
+ }
engine.addImportPath(pluginImportPath);
}
// Merge file.
QStringList mergeDependencies;
- QString mergeComponents;
+ QByteArray mergeComponents;
if (!mergeFile.isEmpty()) {
const QStringList merge = readQmlTypes(mergeFile);
if (!merge.isEmpty()) {
- QRegularExpression re("(\\w+\\.*\\w*\\s*\\d+\\.\\d+)");
+ static const QRegularExpression re("(\\w+\\.*\\w*\\s*\\d+\\.\\d+)");
QRegularExpressionMatchIterator i = re.globalMatch(merge[1]);
while (i.hasNext()) {
QRegularExpressionMatch m = i.next();
mergeDependencies << m.captured(1);
}
- mergeComponents = merge [2];
+ mergeComponents = merge[2].toUtf8();
}
}
@@ -1233,7 +1214,7 @@ int main(int argc, char *argv[])
// load the QtQml builtins and the dependencies
{
QByteArray code(qtQmlImportString.toUtf8());
- for (const QString &moduleToImport : qAsConst(dependencies)) {
+ for (const QString &moduleToImport : std::as_const(dependencies)) {
code.append("\nimport ");
code.append(moduleToImport.toUtf8());
}
@@ -1297,7 +1278,7 @@ int main(int argc, char *argv[])
QString::number(qtObjectType.version().minorVersion())).toUtf8();
}
// avoid importing dependencies?
- for (const QString &moduleToImport : qAsConst(dependencies)) {
+ for (const QString &moduleToImport : std::as_const(dependencies)) {
importCode.append("\nimport ");
importCode.append(moduleToImport.toUtf8());
}
@@ -1334,7 +1315,7 @@ int main(int argc, char *argv[])
it->erase(std::unique(it->begin(), it->end()), it->end());
}
- for (const QMetaObject *mo : qAsConst(candidates)) {
+ for (const QMetaObject *mo : std::as_const(candidates)) {
if (mo->className() != QLatin1String("Qt"))
metas.insert(mo);
}
@@ -1348,27 +1329,31 @@ int main(int argc, char *argv[])
QQmlJSStreamWriter qml(&bytes);
qml.writeStartDocument();
- qml.writeLibraryImport(QLatin1String("QtQuick.tooling"), 1, 2);
- qml.write(QString("\n"
+ qml.writeLibraryImport("QtQuick.tooling", 1, 2);
+ qml.write("\n"
"// This file describes the plugin-supplied types contained in the library.\n"
"// It is used for QML tooling purposes only.\n"
"//\n"
"// This file was auto-generated by:\n"
- "// '%1 %2'\n"
+ "// '");
+ qml.write(QFileInfo(args.at(0)).baseName().toUtf8());
+ qml.write(" ");
+ qml.write(args.mid(1).join(QLatin1Char(' ')).toUtf8());
+ qml.write("'\n"
"//\n"
"// qmlplugindump is deprecated! You should use qmltyperegistrar instead.\n"
- "\n").arg(QFileInfo(args.at(0)).baseName(), args.mid(1).join(QLatin1Char(' '))));
+ "\n");
qml.writeStartObject("Module");
// put the metaobjects into a map so they are always dumped in the same order
QMap<QString, const QMetaObject *> nameToMeta;
- for (const QMetaObject *meta : qAsConst(metas))
+ for (const QMetaObject *meta : std::as_const(metas))
nameToMeta.insert(convertToId(meta), meta);
Dumper dumper(&qml);
if (relocatable)
dumper.setRelocatableModuleUri(pluginImportUri);
- for (const QMetaObject *meta : qAsConst(nameToMeta)) {
+ for (const QMetaObject *meta : std::as_const(nameToMeta)) {
dumper.dump(QQmlEnginePrivate::get(&engine), meta, uncreatableMetas.contains(meta), singletonMetas.contains(meta));
}
diff --git a/tools/qmlplugindump/qmltypereader.cpp b/tools/qmlplugindump/qmltypereader.cpp
index 06f6086d4a..ade3909253 100644
--- a/tools/qmlplugindump/qmltypereader.cpp
+++ b/tools/qmlplugindump/qmltypereader.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "qmltypereader.h"
diff --git a/tools/qmlplugindump/qmltypereader.h b/tools/qmlplugindump/qmltypereader.h
index bb31cf43e1..f1f232b9fd 100644
--- a/tools/qmlplugindump/qmltypereader.h
+++ b/tools/qmlplugindump/qmltypereader.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef QMLTYPEREADER_H
#define QMLTYPEREADER_H
diff --git a/tools/qmlpreview/.prev_CMakeLists.txt b/tools/qmlpreview/.prev_CMakeLists.txt
deleted file mode 100644
index 61efd03729..0000000000
--- a/tools/qmlpreview/.prev_CMakeLists.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-# Generated from qmlpreview.pro.
-
-#####################################################################
-## qmlpreview Tool:
-#####################################################################
-
-qt_get_tool_target_name(target_name qmlpreview)
-qt_internal_add_tool(${target_name}
- TARGET_DESCRIPTION "QML Preview"
- SOURCES
- main.cpp
- qmlpreviewapplication.cpp qmlpreviewapplication.h
- qmlpreviewfilesystemwatcher.cpp qmlpreviewfilesystemwatcher.h
- PUBLIC_LIBRARIES
- Qt::Network
- Qt::QmlDebugPrivate
-)
-
-#### Keys ignored in scope 1:.:.:qmlpreview.pro:<TRUE>:
-# QMAKE_TARGET_DESCRIPTION = "QML" "Preview"
diff --git a/tools/qmlpreview/CMakeLists.txt b/tools/qmlpreview/CMakeLists.txt
index 4c347a404e..eb39cbd8cd 100644
--- a/tools/qmlpreview/CMakeLists.txt
+++ b/tools/qmlpreview/CMakeLists.txt
@@ -1,21 +1,25 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qmlpreview.pro.
#####################################################################
-## qmlpreview Tool:
+## qmlpreview App:
#####################################################################
-qt_get_tool_target_name(target_name qmlpreview)
-qt_internal_add_tool(${target_name}
+qt_internal_add_app(qmlpreview
TARGET_DESCRIPTION "QML Preview"
- TOOLS_TARGET Qml # special case
SOURCES
main.cpp
qmlpreviewapplication.cpp qmlpreviewapplication.h
qmlpreviewfilesystemwatcher.cpp qmlpreviewfilesystemwatcher.h
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Network
Qt::QmlDebugPrivate
)
+qt_internal_return_unless_building_tools()
+
+set_target_properties(qmlpreview PROPERTIES WIN32_EXECUTABLE FALSE)
#### Keys ignored in scope 1:.:.:qmlpreview.pro:<TRUE>:
# QMAKE_TARGET_DESCRIPTION = "QML" "Preview"
diff --git a/tools/qmlpreview/main.cpp b/tools/qmlpreview/main.cpp
index c7a32da258..48c632f19e 100644
--- a/tools/qmlpreview/main.cpp
+++ b/tools/qmlpreview/main.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "qmlpreviewapplication.h"
diff --git a/tools/qmlpreview/qmlpreviewapplication.cpp b/tools/qmlpreview/qmlpreviewapplication.cpp
index 2568425573..e925ef036e 100644
--- a/tools/qmlpreview/qmlpreviewapplication.cpp
+++ b/tools/qmlpreview/qmlpreviewapplication.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "qmlpreviewapplication.h"
@@ -39,6 +14,7 @@
#include <QtCore/QCommandLineParser>
#include <QtCore/QTemporaryFile>
#include <QtCore/QUrl>
+#include <QtCore/QLibraryInfo>
QmlPreviewApplication::QmlPreviewApplication(int &argc, char **argv) :
QCoreApplication(argc, argv),
diff --git a/tools/qmlpreview/qmlpreviewapplication.h b/tools/qmlpreview/qmlpreviewapplication.h
index 51a70cbac3..7e71c2dac5 100644
--- a/tools/qmlpreview/qmlpreviewapplication.h
+++ b/tools/qmlpreview/qmlpreviewapplication.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef QMLPREVIEWAPPLICATION_H
#define QMLPREVIEWAPPLICATION_H
diff --git a/tools/qmlpreview/qmlpreviewfilesystemwatcher.cpp b/tools/qmlpreview/qmlpreviewfilesystemwatcher.cpp
index 78a6ccead3..4db2c03b20 100644
--- a/tools/qmlpreview/qmlpreviewfilesystemwatcher.cpp
+++ b/tools/qmlpreview/qmlpreviewfilesystemwatcher.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "qmlpreviewfilesystemwatcher.h"
@@ -144,7 +119,7 @@ void QmlPreviewFileSystemWatcher::onDirectoryChanged(const QString &path)
toReadd.removeOne(rejected);
// If we've successfully added the file, that means it was deleted and replaced.
- for (const QString &reAdded : qAsConst(toReadd))
+ for (const QString &reAdded : std::as_const(toReadd))
emit fileChanged(reAdded);
}
}
diff --git a/tools/qmlpreview/qmlpreviewfilesystemwatcher.h b/tools/qmlpreview/qmlpreviewfilesystemwatcher.h
index a7ead641d8..6304405b9d 100644
--- a/tools/qmlpreview/qmlpreviewfilesystemwatcher.h
+++ b/tools/qmlpreview/qmlpreviewfilesystemwatcher.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef QMLPREVIEWFILESYSTEMWATCHER_H
#define QMLPREVIEWFILESYSTEMWATCHER_H
@@ -48,7 +23,7 @@ public:
void removeDirectory(const QString &file);
bool watchesDirectory(const QString &file) const;
-signals:
+Q_SIGNALS:
void fileChanged(const QString &path);
void directoryChanged(const QString &path);
diff --git a/tools/qmlprofiler/.prev_CMakeLists.txt b/tools/qmlprofiler/.prev_CMakeLists.txt
deleted file mode 100644
index 51418f62dc..0000000000
--- a/tools/qmlprofiler/.prev_CMakeLists.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-# Generated from qmlprofiler.pro.
-
-#####################################################################
-## qmlprofiler Tool:
-#####################################################################
-
-qt_get_tool_target_name(target_name qmlprofiler)
-qt_internal_add_tool(${target_name}
- TARGET_DESCRIPTION "QML Profiler"
- SOURCES
- commandlistener.cpp commandlistener.h
- constants.h
- main.cpp
- qmlprofilerapplication.cpp qmlprofilerapplication.h
- qmlprofilerclient.cpp qmlprofilerclient.h
- qmlprofilerdata.cpp qmlprofilerdata.h
- PUBLIC_LIBRARIES
- Qt::Network
- Qt::QmlDebugPrivate
-)
-
-#### Keys ignored in scope 1:.:.:qmlprofiler.pro:<TRUE>:
-# QMAKE_TARGET_DESCRIPTION = "QML" "Profiler"
diff --git a/tools/qmlprofiler/CMakeLists.txt b/tools/qmlprofiler/CMakeLists.txt
index c2e5c111ae..72a2487a47 100644
--- a/tools/qmlprofiler/CMakeLists.txt
+++ b/tools/qmlprofiler/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qmlprofiler.pro.
#####################################################################
@@ -15,10 +18,11 @@ qt_internal_add_tool(${target_name}
qmlprofilerapplication.cpp qmlprofilerapplication.h
qmlprofilerclient.cpp qmlprofilerclient.h
qmlprofilerdata.cpp qmlprofilerdata.h
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Network
Qt::QmlDebugPrivate
)
+qt_internal_return_unless_building_tools()
#### Keys ignored in scope 1:.:.:qmlprofiler.pro:<TRUE>:
# QMAKE_TARGET_DESCRIPTION = "QML" "Profiler"
diff --git a/tools/qmlprofiler/commandlistener.cpp b/tools/qmlprofiler/commandlistener.cpp
index 079ea9b3bd..3d1fc2d311 100644
--- a/tools/qmlprofiler/commandlistener.cpp
+++ b/tools/qmlprofiler/commandlistener.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "commandlistener.h"
#include "constants.h"
diff --git a/tools/qmlprofiler/commandlistener.h b/tools/qmlprofiler/commandlistener.h
index 2a994bf449..65ba3682bd 100644
--- a/tools/qmlprofiler/commandlistener.h
+++ b/tools/qmlprofiler/commandlistener.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef COMMANDLISTENER_H
#define COMMANDLISTENER_H
@@ -36,7 +11,7 @@ class CommandListener : public QObject {
public:
void readCommand();
-signals:
+Q_SIGNALS:
void command(const QString &command);
};
diff --git a/tools/qmlprofiler/constants.h b/tools/qmlprofiler/constants.h
index 8c3e16a8a7..6fdcbd8c82 100644
--- a/tools/qmlprofiler/constants.h
+++ b/tools/qmlprofiler/constants.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef CONSTANTS_H
#define CONSTANTS_H
diff --git a/tools/qmlprofiler/main.cpp b/tools/qmlprofiler/main.cpp
index c7cb979ff8..e89eecbef2 100644
--- a/tools/qmlprofiler/main.cpp
+++ b/tools/qmlprofiler/main.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "commandlistener.h"
#include "qmlprofilerapplication.h"
diff --git a/tools/qmlprofiler/qmlprofilerapplication.cpp b/tools/qmlprofiler/qmlprofilerapplication.cpp
index 4d97efafb4..9447333700 100644
--- a/tools/qmlprofiler/qmlprofilerapplication.cpp
+++ b/tools/qmlprofiler/qmlprofilerapplication.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "qmlprofilerapplication.h"
#include "constants.h"
@@ -36,6 +11,7 @@
#include <QtCore/QDebug>
#include <QtCore/QCommandLineParser>
#include <QtCore/QTemporaryFile>
+#include <QtCore/QLibraryInfo>
#include <iostream>
@@ -408,7 +384,7 @@ void QmlProfilerApplication::userCommand(const QString &command)
} else if (m_profilerData->isEmpty()) {
prompt(tr("No data was recorded so far."));
} else {
- m_interactiveOutputFile = args.length() > 0 ? args.at(0).toString() : m_outputFile;
+ m_interactiveOutputFile = args.size() > 0 ? args.at(0).toString() : m_outputFile;
if (checkOutputFile(REQUEST_OUTPUT_FILE))
output();
}
@@ -425,7 +401,7 @@ void QmlProfilerApplication::userCommand(const QString &command)
if (!m_recording && m_profilerData->isEmpty()) {
prompt(tr("No data was recorded so far."));
} else {
- m_interactiveOutputFile = args.length() > 0 ? args.at(0).toString() : m_outputFile;
+ m_interactiveOutputFile = args.size() > 0 ? args.at(0).toString() : m_outputFile;
if (checkOutputFile(REQUEST_FLUSH_FILE))
flush();
}
diff --git a/tools/qmlprofiler/qmlprofilerapplication.h b/tools/qmlprofiler/qmlprofilerapplication.h
index e069659af3..b02faa7c9d 100644
--- a/tools/qmlprofiler/qmlprofilerapplication.h
+++ b/tools/qmlprofiler/qmlprofilerapplication.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef QMLPROFILERAPPLICATION_H
#define QMLPROFILERAPPLICATION_H
@@ -62,7 +37,7 @@ public:
void notifyTraceStarted();
void outputData();
-signals:
+Q_SIGNALS:
void readyForCommand();
private:
diff --git a/tools/qmlprofiler/qmlprofilerclient.cpp b/tools/qmlprofiler/qmlprofilerclient.cpp
index f6cc6f39fe..e8c565261c 100644
--- a/tools/qmlprofiler/qmlprofilerclient.cpp
+++ b/tools/qmlprofiler/qmlprofilerclient.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "qmlprofilerclient.h"
#include "qmlprofilerdata.h"
diff --git a/tools/qmlprofiler/qmlprofilerclient.h b/tools/qmlprofiler/qmlprofilerclient.h
index 7355688222..b6558869bc 100644
--- a/tools/qmlprofiler/qmlprofilerclient.h
+++ b/tools/qmlprofiler/qmlprofilerclient.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef QMLPROFILERCLIENT_H
#define QMLPROFILERCLIENT_H
@@ -43,7 +18,7 @@ class QmlProfilerClient : public QQmlProfilerClient
public:
QmlProfilerClient(QQmlDebugConnection *connection, QmlProfilerData *data);
-signals:
+Q_SIGNALS:
void enabledChanged(bool enabled);
void error(const QString &error);
diff --git a/tools/qmlprofiler/qmlprofilerdata.cpp b/tools/qmlprofiler/qmlprofilerdata.cpp
index 8803170ff2..c9e917cc00 100644
--- a/tools/qmlprofiler/qmlprofilerdata.cpp
+++ b/tools/qmlprofiler/qmlprofilerdata.cpp
@@ -1,41 +1,14 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "qmlprofilerdata.h"
-#include <QStringList>
-#include <QUrl>
-#include <QHash>
-#include <QFile>
-#include <QXmlStreamReader>
-#include <QRegularExpression>
-#include <QQueue>
-#include <QStack>
+#include <QtCore/qfile.h>
+#include <QtCore/qqueue.h>
+#include <QtCore/qregularexpression.h>
+#include <QtCore/qurl.h>
+#include <QtCore/qxmlstream.h>
+#include <QtCore/qxpfunctional.h>
#include <limits>
@@ -236,7 +209,7 @@ void QmlProfilerData::computeQmlTime()
qint64 level0Start = -1;
int level = 0;
- for (const QQmlProfilerEvent &event : qAsConst(d->events)) {
+ for (const QQmlProfilerEvent &event : std::as_const(d->events)) {
const QQmlProfilerEventType &type = d->eventTypes.at(event.typeIndex());
if (type.message() != MaximumMessage)
continue;
@@ -273,7 +246,7 @@ bool compareStartTimes(const QQmlProfilerEvent &t1, const QQmlProfilerEvent &t2)
void QmlProfilerData::sortStartTimes()
{
- if (d->events.count() < 2)
+ if (d->events.size() < 2)
return;
// assuming startTimes is partially sorted
@@ -400,6 +373,132 @@ private:
QXmlStreamWriter stream;
};
+struct DataIterator
+{
+ DataIterator(
+ const QmlProfilerDataPrivate *d,
+ qxp::function_ref<void(const QQmlProfilerEvent &, qint64)> &&sendEvent)
+ : d(d)
+ , sendEvent(std::move(sendEvent))
+ {}
+
+ void run();
+
+private:
+ void handleRangeEvent(const QQmlProfilerEvent &event, const QQmlProfilerEventType &type);
+ void sendPending();
+ void endLevel0();
+
+ const QmlProfilerDataPrivate *d = nullptr;
+ const qxp::function_ref<void(const QQmlProfilerEvent &, qint64)> sendEvent;
+
+ QQueue<QQmlProfilerEvent> pointEvents;
+ QList<QQmlProfilerEvent> rangeStarts[MaximumRangeType];
+ QList<qint64> rangeEnds[MaximumRangeType];
+
+ int level = 0;
+};
+
+void DataIterator::handleRangeEvent(
+ const QQmlProfilerEvent &event, const QQmlProfilerEventType &type)
+{
+ QList<QQmlProfilerEvent> &starts = rangeStarts[type.rangeType()];
+ switch (event.rangeStage()) {
+ case RangeStart: {
+ ++level;
+ starts.append(event);
+ break;
+ }
+ case RangeEnd: {
+ const qint64 invalidTimestamp = -1;
+ QList<qint64> &ends = rangeEnds[type.rangeType()];
+
+ // -1 because all valid timestamps are >= 0.
+ ends.resize(starts.size(), invalidTimestamp);
+
+ qsizetype i = starts.size();
+ while (ends[--i] != invalidTimestamp) {}
+
+ Q_ASSERT(i >= 0);
+ Q_ASSERT(starts[i].timestamp() <= event.timestamp());
+
+ ends[i] = event.timestamp();
+ if (--level == 0)
+ endLevel0();
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void DataIterator::sendPending()
+{
+ // Send all pending events in the order of their start times.
+
+ qsizetype index[MaximumRangeType] = { 0, 0, 0, 0, 0, 0 };
+ while (true) {
+
+ // Find the range type with the minimum start time.
+ qsizetype minimum = MaximumRangeType;
+ qint64 minimumTime = std::numeric_limits<qint64>::max();
+ for (qsizetype i = 0; i < MaximumRangeType; ++i) {
+ const QList<QQmlProfilerEvent> &starts = rangeStarts[i];
+ if (starts.size() == index[i])
+ continue;
+ const qint64 timestamp = starts[index[i]].timestamp();
+ if (timestamp < minimumTime) {
+ minimumTime = timestamp;
+ minimum = i;
+ }
+ }
+ if (minimum == MaximumRangeType)
+ break;
+
+ // Send all point events that happened before the range we've found.
+ while (!pointEvents.isEmpty() && pointEvents.front().timestamp() < minimumTime)
+ sendEvent(pointEvents.dequeue(), 0);
+
+ // Send the range itself
+ sendEvent(rangeStarts[minimum][index[minimum]],
+ rangeEnds[minimum][index[minimum]] - minimumTime);
+
+ // Bump the index so that we don't send the same range again
+ ++index[minimum];
+ }
+}
+
+void DataIterator::endLevel0()
+{
+ sendPending();
+ for (qsizetype i = 0; i < MaximumRangeType; ++i) {
+ rangeStarts[i].clear();
+ rangeEnds[i].clear();
+ }
+}
+
+void DataIterator::run()
+{
+ for (const QQmlProfilerEvent &event : std::as_const(d->events)) {
+ const QQmlProfilerEventType &type = d->eventTypes.at(event.typeIndex());
+ if (type.rangeType() != MaximumRangeType)
+ handleRangeEvent(event, type);
+ else if (level == 0)
+ sendEvent(event, 0);
+ else
+ pointEvents.enqueue(event);
+ }
+
+ for (qsizetype i = 0; i < MaximumRangeType; ++i) {
+ while (rangeEnds[i].size() < rangeStarts[i].size()) {
+ rangeEnds[i].append(d->traceEndTime);
+ --level;
+ }
+ }
+
+ sendPending();
+}
+
bool QmlProfilerData::save(const QString &filename)
{
if (isEmpty()) {
@@ -467,6 +566,7 @@ bool QmlProfilerData::save(const QString &filename)
stream.writeStartElement("profilerDataModel");
auto sendEvent = [&](const QQmlProfilerEvent &event, qint64 duration = 0) {
+ Q_ASSERT(duration >= 0);
const QQmlProfilerEventType &type = d->eventTypes.at(event.typeIndex());
stream.writeStartElement("range");
stream.writeAttribute("startTime", event.timestamp());
@@ -506,74 +606,7 @@ bool QmlProfilerData::save(const QString &filename)
stream.writeEndElement();
};
- QQueue<QQmlProfilerEvent> pointEvents;
- QQueue<QQmlProfilerEvent> rangeStarts[MaximumRangeType];
- QStack<qint64> rangeEnds[MaximumRangeType];
- int level = 0;
-
- auto sendPending = [&]() {
- forever {
- int minimum = MaximumRangeType;
- qint64 minimumTime = std::numeric_limits<qint64>::max();
- for (int i = 0; i < MaximumRangeType; ++i) {
- const QQueue<QQmlProfilerEvent> &starts = rangeStarts[i];
- if (starts.isEmpty())
- continue;
- if (starts.head().timestamp() < minimumTime) {
- minimumTime = starts.head().timestamp();
- minimum = i;
- }
- }
- if (minimum == MaximumRangeType)
- break;
-
- while (!pointEvents.isEmpty() && pointEvents.front().timestamp() < minimumTime)
- sendEvent(pointEvents.dequeue());
-
- sendEvent(rangeStarts[minimum].dequeue(),
- rangeEnds[minimum].pop() - minimumTime);
- }
- };
-
- for (const QQmlProfilerEvent &event : qAsConst(d->events)) {
- const QQmlProfilerEventType &type = d->eventTypes.at(event.typeIndex());
-
- if (type.rangeType() != MaximumRangeType) {
- QQueue<QQmlProfilerEvent> &starts = rangeStarts[type.rangeType()];
- switch (event.rangeStage()) {
- case RangeStart: {
- ++level;
- starts.enqueue(event);
- break;
- }
- case RangeEnd: {
- QStack<qint64> &ends = rangeEnds[type.rangeType()];
- if (starts.length() > ends.length()) {
- ends.push(event.timestamp());
- if (--level == 0)
- sendPending();
- }
- break;
- }
- default:
- break;
- }
- } else {
- if (level == 0)
- sendEvent(event);
- else
- pointEvents.enqueue(event);
- }
- }
-
- for (int i = 0; i < MaximumRangeType; ++i) {
- while (rangeEnds[i].length() < rangeStarts[i].length()) {
- rangeEnds[i].push(d->traceEndTime);
- --level;
- }
- }
-
- sendPending();
+ DataIterator(d, std::move(sendEvent)).run();
stream.writeEndElement(); // profilerDataModel
@@ -622,7 +655,7 @@ void QmlProfilerData::setState(QmlProfilerData::State state)
int QmlProfilerData::numLoadedEventTypes() const
{
- return d->eventTypes.length();
+ return d->eventTypes.size();
}
#include "moc_qmlprofilerdata.cpp"
diff --git a/tools/qmlprofiler/qmlprofilerdata.h b/tools/qmlprofiler/qmlprofilerdata.h
index 16ebc37f28..95b8869fac 100644
--- a/tools/qmlprofiler/qmlprofilerdata.h
+++ b/tools/qmlprofiler/qmlprofilerdata.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef QMLPROFILERDATA_H
#define QMLPROFILERDATA_H
@@ -47,7 +22,7 @@ public:
Done
};
- explicit QmlProfilerData(QObject *parent = 0);
+ explicit QmlProfilerData(QObject *parent = nullptr);
~QmlProfilerData();
int numLoadedEventTypes() const override;
@@ -70,7 +45,7 @@ public:
void complete();
bool save(const QString &filename);
-signals:
+Q_SIGNALS:
void error(QString);
void stateChanged();
void dataReady();
diff --git a/tools/qmlscene/.prev_CMakeLists.txt b/tools/qmlscene/.prev_CMakeLists.txt
deleted file mode 100644
index 5c29546d60..0000000000
--- a/tools/qmlscene/.prev_CMakeLists.txt
+++ /dev/null
@@ -1,37 +0,0 @@
-# Generated from qmlscene.pro.
-
-#####################################################################
-## qmlscene Tool:
-#####################################################################
-
-qt_get_tool_target_name(target_name qmlscene)
-qt_internal_add_tool(${target_name}
- TARGET_DESCRIPTION "QML Scene Viewer"
- SOURCES
- main.cpp
- DEFINES
- QML_RUNTIME_TESTING
- PUBLIC_LIBRARIES
- Qt::CorePrivate
- Qt::Gui
- Qt::GuiPrivate
- Qt::Qml
- Qt::Quick
- Qt::QuickPrivate
-)
-
-#### Keys ignored in scope 1:.:.:qmlscene.pro:<TRUE>:
-# QMAKE_TARGET_DESCRIPTION = "QML" "Scene" "Viewer"
-
-## Scopes:
-#####################################################################
-
-qt_internal_extend_target(${target_name} CONDITION TARGET Qt::Widgets
- PUBLIC_LIBRARIES
- Qt::Widgets
-)
-
-qt_internal_extend_target(${target_name} CONDITION QT_FEATURE_qml_debug
- DEFINES
- QT_QML_DEBUG_NO_WARNING
-)
diff --git a/tools/qmlscene/CMakeLists.txt b/tools/qmlscene/CMakeLists.txt
index 4566a60441..305420d471 100644
--- a/tools/qmlscene/CMakeLists.txt
+++ b/tools/qmlscene/CMakeLists.txt
@@ -1,18 +1,17 @@
-# Generated from qmlscene.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
-## qmlscene Tool:
+## qmlscene App:
#####################################################################
-qt_get_tool_target_name(target_name qmlscene)
-qt_internal_add_tool(${target_name}
+qt_internal_add_app(qmlscene
TARGET_DESCRIPTION "QML Scene Viewer"
- TOOLS_TARGET Qml # special case
SOURCES
main.cpp
DEFINES
QML_RUNTIME_TESTING
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::CorePrivate
Qt::Gui
Qt::GuiPrivate
@@ -21,18 +20,14 @@ qt_internal_add_tool(${target_name}
Qt::QuickPrivate
)
-#### Keys ignored in scope 1:.:.:qmlscene.pro:<TRUE>:
-# QMAKE_TARGET_DESCRIPTION = "QML" "Scene" "Viewer"
-
-## Scopes:
-#####################################################################
+set_target_properties(qmlscene PROPERTIES WIN32_EXECUTABLE FALSE)
-qt_internal_extend_target(${target_name} CONDITION TARGET Qt::Widgets
+qt_internal_extend_target(qmlscene CONDITION TARGET Qt::Widgets
PUBLIC_LIBRARIES
Qt::Widgets
)
-qt_internal_extend_target(${target_name} CONDITION QT_FEATURE_qml_debug
+qt_internal_extend_target(qmlscene CONDITION QT_FEATURE_qml_debug
DEFINES
QT_QML_DEBUG_NO_WARNING
)
diff --git a/tools/qmlscene/main.cpp b/tools/qmlscene/main.cpp
index de00522d48..123844ebb3 100644
--- a/tools/qmlscene/main.cpp
+++ b/tools/qmlscene/main.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QtCore/qabstractanimation.h>
#include <QtCore/qdir.h>
@@ -34,6 +9,7 @@
#include <QtCore/qscopedpointer.h>
#include <QtCore/qtextstream.h>
#include <QtCore/qregularexpression.h>
+#include <QtCore/qloggingcategory.h>
#include <QtGui/QGuiApplication>
@@ -58,6 +34,8 @@
#include <QtCore/QTranslator>
#include <QtCore/QLibraryInfo>
+Q_LOGGING_CATEGORY(lcQmlsceneDeprecated, "qt.tools.qmlscene.deprecated")
+
#ifdef QML_RUNTIME_TESTING
class RenderStatistics
{
@@ -108,7 +86,7 @@ void RenderStatistics::updateStats()
void RenderStatistics::printTotalStats()
{
- int count = timePerFrame.count();
+ int count = timePerFrame.size();
if (count == 0)
return;
@@ -333,7 +311,7 @@ static void loadDummyDataFiles(QQmlEngine &engine, const QString& directory)
if (dummyData) {
fprintf(stderr, "Loaded dummy data: %s\n", qPrintable(dir.filePath(qml)));
- qml.truncate(qml.length()-4);
+ qml.truncate(qml.size()-4);
engine.rootContext()->setContextProperty(qml, dummyData);
dummyData->setParent(&engine);
}
@@ -359,7 +337,7 @@ static void usage()
" ........ (remove AA_ShareOpenGLContexts)");
puts(" --desktop......................... Force use of desktop GL (AA_UseDesktopOpenGL)");
puts(" --gles............................ Force use of GLES (AA_UseOpenGLES)");
- puts(" --software........................ Force use of software rendering (AA_UseOpenGLES)");
+ puts(" --software........................ Force use of software rendering (AA_UseSoftwareOpenGL)");
puts(" --verbose......................... Print version and graphical diagnostics for the run-time");
#ifdef QT_WIDGETS_LIB
puts(" --apptype [gui|widgets] .......... Select which application class to use. Default is widgets.");
@@ -419,8 +397,7 @@ static QQuickWindow::TextRenderType parseTextRenderType(const QString &renderTyp
usage();
- Q_UNREACHABLE();
- return QQuickWindow::QtTextRendering;
+ Q_UNREACHABLE_RETURN(QQuickWindow::QtTextRendering);
}
int main(int argc, char ** argv)
@@ -431,6 +408,8 @@ int main(int argc, char ** argv)
QStringList customSelectors;
QStringList pluginPaths;
+ qCWarning(lcQmlsceneDeprecated()) << "Warning: qmlscene is deprecated and will be removed in a future version of Qt. Please use qml instead.";
+
// Parse arguments for application attributes to be applied before Q[Gui]Application creation.
for (int i = 1; i < argc; ++i) {
const char *arg = argv[i];
@@ -474,7 +453,7 @@ int main(int argc, char ** argv)
}
QSurfaceFormat::setDefaultFormat(surfaceFormat);
- for (Qt::ApplicationAttribute a : qAsConst(options.applicationAttributes))
+ for (Qt::ApplicationAttribute a : std::as_const(options.applicationAttributes))
QCoreApplication::setAttribute(a);
QScopedPointer<QGuiApplication> app;
#ifdef QT_WIDGETS_LIB
diff --git a/tools/qmltc/CMakeLists.txt b/tools/qmltc/CMakeLists.txt
new file mode 100644
index 0000000000..42e47e24da
--- /dev/null
+++ b/tools/qmltc/CMakeLists.txt
@@ -0,0 +1,27 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_get_tool_target_name(target_name qmltc)
+qt_internal_add_tool(${target_name}
+ TARGET_DESCRIPTION "QML type compiler"
+ TOOLS_TARGET Qml
+ SOURCES
+ main.cpp
+ qmltccommandlineutils.h qmltccommandlineutils.cpp
+ qmltcoutputprimitives.h
+ qmltccodewriter.h qmltccodewriter.cpp
+ qmltcoutputir.h
+ qmltctyperesolver.h qmltctyperesolver.cpp
+ qmltcvisitor.h qmltcvisitor.cpp
+ qmltccompiler.h qmltccompiler.cpp
+ qmltccompilerpieces.h qmltccompilerpieces.cpp
+ qmltcpropertyutils.h
+ DEFINES
+ QT_NO_CAST_FROM_ASCII
+ QT_NO_CAST_TO_ASCII
+ LIBRARIES
+ Qt::Core
+ Qt::QmlPrivate
+ Qt::QmlCompilerPrivate
+)
+qt_internal_return_unless_building_tools()
diff --git a/tools/qmltc/main.cpp b/tools/qmltc/main.cpp
new file mode 100644
index 0000000000..a310c3d3c6
--- /dev/null
+++ b/tools/qmltc/main.cpp
@@ -0,0 +1,312 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "qmltccommandlineutils.h"
+#include "qmltcvisitor.h"
+#include "qmltctyperesolver.h"
+
+#include "qmltccompiler.h"
+
+#include <private/qqmljscompiler_p.h>
+#include <private/qqmljsresourcefilemapper_p.h>
+
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qurl.h>
+#include <QtCore/qhashfunctions.h>
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qlibraryinfo.h>
+#include <QtCore/qcommandlineparser.h>
+#include <QtCore/qregularexpression.h>
+
+#include <QtQml/private/qqmljslexer_p.h>
+#include <QtQml/private/qqmljsparser_p.h>
+#include <QtQml/private/qqmljsengine_p.h>
+#include <QtQml/private/qqmljsastvisitor_p.h>
+#include <QtQml/private/qqmljsast_p.h>
+#include <QtQml/private/qqmljsdiagnosticmessage_p.h>
+#include <QtQmlCompiler/qqmlsa.h>
+#include <QtQmlCompiler/private/qqmljsliteralbindingcheck_p.h>
+
+#include <cstdlib> // EXIT_SUCCESS, EXIT_FAILURE
+
+using namespace Qt::StringLiterals;
+
+void setupLogger(QQmlJSLogger &logger) // prepare logger to work with compiler
+{
+ for (const QQmlJS::LoggerCategory &category : logger.categories()) {
+ if (category.id() == qmlUnusedImports)
+ continue;
+ logger.setCategoryLevel(category.id(), QtCriticalMsg);
+ logger.setCategoryIgnored(category.id(), false);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ // Produce reliably the same output for the same input by disabling QHash's
+ // random seeding.
+ QHashSeed::setDeterministicGlobalSeed();
+ QCoreApplication app(argc, argv);
+ QCoreApplication::setApplicationName(u"qmltc"_s);
+ QCoreApplication::setApplicationVersion(QStringLiteral(QT_VERSION_STR));
+
+ // command-line parsing:
+ QCommandLineParser parser;
+ parser.addHelpOption();
+ parser.addVersionOption();
+
+ QCommandLineOption bareOption {
+ u"bare"_s,
+ QCoreApplication::translate(
+ "main", "Do not include default import directories. This may be used to run "
+ "qmltc on a project using a different Qt version.")
+ };
+ parser.addOption(bareOption);
+
+ QCommandLineOption importPathOption {
+ u"I"_s, QCoreApplication::translate("main", "Look for QML modules in specified directory"),
+ QCoreApplication::translate("main", "import directory")
+ };
+ parser.addOption(importPathOption);
+ QCommandLineOption qmldirOption {
+ u"i"_s, QCoreApplication::translate("main", "Include extra qmldir files"),
+ QCoreApplication::translate("main", "qmldir file")
+ };
+ parser.addOption(qmldirOption);
+ QCommandLineOption outputCppOption {
+ u"impl"_s, QCoreApplication::translate("main", "Generated C++ source file path"),
+ QCoreApplication::translate("main", "cpp path")
+ };
+ parser.addOption(outputCppOption);
+ QCommandLineOption outputHOption {
+ u"header"_s, QCoreApplication::translate("main", "Generated C++ header file path"),
+ QCoreApplication::translate("main", "h path")
+ };
+ parser.addOption(outputHOption);
+ QCommandLineOption resourceOption {
+ u"resource"_s,
+ QCoreApplication::translate(
+ "main", "Qt resource file that might later contain one of the compiled files"),
+ QCoreApplication::translate("main", "resource file name")
+ };
+ parser.addOption(resourceOption);
+ QCommandLineOption metaResourceOption {
+ u"meta-resource"_s,
+ QCoreApplication::translate("main", "Qt meta information file (in .qrc format)"),
+ QCoreApplication::translate("main", "meta file name")
+ };
+ parser.addOption(metaResourceOption);
+ QCommandLineOption namespaceOption {
+ u"namespace"_s, QCoreApplication::translate("main", "Namespace of the generated C++ code"),
+ QCoreApplication::translate("main", "namespace")
+ };
+ parser.addOption(namespaceOption);
+ QCommandLineOption moduleOption{
+ u"module"_s,
+ QCoreApplication::translate("main",
+ "Name of the QML module that this QML code belongs to."),
+ QCoreApplication::translate("main", "module")
+ };
+ parser.addOption(moduleOption);
+ QCommandLineOption exportOption{ u"export"_s,
+ QCoreApplication::translate(
+ "main", "Export macro used in the generated C++ code"),
+ QCoreApplication::translate("main", "export") };
+ parser.addOption(exportOption);
+ QCommandLineOption exportIncludeOption{
+ u"exportInclude"_s,
+ QCoreApplication::translate(
+ "main", "Header defining the export macro to be used in the generated C++ code"),
+ QCoreApplication::translate("main", "exportInclude")
+ };
+ parser.addOption(exportIncludeOption);
+
+ parser.process(app);
+
+ const QStringList sources = parser.positionalArguments();
+ if (sources.size() != 1) {
+ if (sources.isEmpty()) {
+ parser.showHelp();
+ } else {
+ fprintf(stderr, "%s\n",
+ qPrintable(u"Too many input files specified: '"_s + sources.join(u"' '"_s)
+ + u'\''));
+ }
+ return EXIT_FAILURE;
+ }
+ const QString inputFile = sources.first();
+
+ QString url = parseUrlArgument(inputFile);
+ if (url.isNull())
+ return EXIT_FAILURE;
+ if (!url.endsWith(u".qml")) {
+ fprintf(stderr, "Non-QML file passed as input\n");
+ return EXIT_FAILURE;
+ }
+
+ static QRegularExpression nameChecker(u"^[a-zA-Z_][a-zA-Z0-9_]*\\.qml$"_s);
+ if (auto match = nameChecker.match(QUrl(url).fileName()); !match.hasMatch()) {
+ fprintf(stderr,
+ "The given QML filename is unsuited for type compilation: the name must consist of "
+ "letters, digits and underscores, starting with "
+ "a letter or an underscore and ending in '.qml'!\n");
+ return EXIT_FAILURE;
+ }
+
+ QString sourceCode = loadUrl(url);
+ if (sourceCode.isEmpty())
+ return EXIT_FAILURE;
+
+ QString implicitImportDirectory = getImplicitImportDirectory(url);
+ if (implicitImportDirectory.isEmpty())
+ return EXIT_FAILURE;
+
+ QStringList importPaths;
+
+ if (parser.isSet(resourceOption)) {
+ importPaths.append(QLatin1String(":/qt-project.org/imports"));
+ importPaths.append(QLatin1String(":/qt/qml"));
+ };
+
+ if (parser.isSet(importPathOption))
+ importPaths.append(parser.values(importPathOption));
+
+ if (!parser.isSet(bareOption))
+ importPaths.append(QLibraryInfo::path(QLibraryInfo::QmlImportsPath));
+
+ QStringList qmldirFiles = parser.values(qmldirOption);
+
+ QString outputCppFile;
+ if (!parser.isSet(outputCppOption)) {
+ outputCppFile = url.first(url.size() - 3) + u"cpp"_s;
+ } else {
+ outputCppFile = parser.value(outputCppOption);
+ }
+
+ QString outputHFile;
+ if (!parser.isSet(outputHOption)) {
+ outputHFile = url.first(url.size() - 3) + u"h"_s;
+ } else {
+ outputHFile = parser.value(outputHOption);
+ }
+
+ if (!parser.isSet(resourceOption)) {
+ fprintf(stderr, "No resource paths for file: %s\n", qPrintable(inputFile));
+ return EXIT_FAILURE;
+ }
+
+ // main logic:
+ QQmlJS::Engine engine;
+ QQmlJS::Lexer lexer(&engine);
+ lexer.setCode(sourceCode, /*lineno = */ 1);
+ QQmlJS::Parser qmlParser(&engine);
+ if (!qmlParser.parse()) {
+ const auto diagnosticMessages = qmlParser.diagnosticMessages();
+ for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) {
+ fprintf(stderr, "%s\n",
+ qPrintable(QStringLiteral("%1:%2:%3: %4")
+ .arg(inputFile)
+ .arg(m.loc.startLine)
+ .arg(m.loc.startColumn)
+ .arg(m.message)));
+ }
+ return EXIT_FAILURE;
+ }
+
+ const QStringList resourceFiles = parser.values(resourceOption);
+ QQmlJSResourceFileMapper mapper(resourceFiles);
+ const QStringList metaResourceFiles = parser.values(metaResourceOption);
+ QQmlJSResourceFileMapper metaDataMapper(metaResourceFiles);
+
+ const auto firstQml = [](const QStringList &paths) {
+ auto it = std::find_if(paths.cbegin(), paths.cend(),
+ [](const QString &x) { return x.endsWith(u".qml"_s); });
+ if (it == paths.cend())
+ return QString();
+ return *it;
+ };
+ // verify that we can map current file to qrc (then use the qrc path later)
+ const QStringList paths = mapper.resourcePaths(QQmlJSResourceFileMapper::localFileFilter(url));
+ if (paths.isEmpty()) {
+ fprintf(stderr, "Failed to find a resource path for file: %s\n", qPrintable(inputFile));
+ return EXIT_FAILURE;
+ } else if (paths.size() > 1) {
+ bool good = !firstQml(paths).isEmpty();
+ good &= std::any_of(paths.cbegin(), paths.cend(),
+ [](const QString &x) { return x.endsWith(u".h"_s); });
+ if (!good || paths.size() > 2) {
+ fprintf(stderr, "Unexpected resource paths for file: %s\n", qPrintable(inputFile));
+ return EXIT_FAILURE;
+ }
+ }
+
+ QmltcCompilerInfo info;
+ info.outputCppFile = parser.value(outputCppOption);
+ info.outputHFile = parser.value(outputHOption);
+ info.resourcePath = firstQml(paths);
+ info.outputNamespace = parser.value(namespaceOption);
+ info.exportMacro = parser.value(exportOption);
+ info.exportInclude = parser.value(exportIncludeOption);
+
+ if (info.outputCppFile.isEmpty()) {
+ fprintf(stderr, "An output C++ file is required. Pass one using --impl");
+ return EXIT_FAILURE;
+ }
+ if (info.outputHFile.isEmpty()) {
+ fprintf(stderr, "An output C++ header file is required. Pass one using --header");
+ return EXIT_FAILURE;
+ }
+
+ QQmlJSImporter importer { importPaths, &mapper };
+ importer.setMetaDataMapper(&metaDataMapper);
+ auto qmltcVisitor = [](QQmlJS::AST::Node *rootNode, QQmlJSImporter *self,
+ const QQmlJSImporter::ImportVisitorPrerequisites &p) {
+ QmltcVisitor v(p.m_target, self, p.m_logger, p.m_implicitImportDirectory, p.m_qmldirFiles);
+ QQmlJS::AST::Node::accept(rootNode, &v);
+ };
+ importer.setImportVisitor(qmltcVisitor);
+
+ QQmlJSLogger logger;
+ logger.setFileName(url);
+ logger.setCode(sourceCode);
+ setupLogger(logger);
+
+ auto currentScope = QQmlJSScope::create();
+ if (parser.isSet(moduleOption))
+ currentScope->setOwnModuleName(parser.value(moduleOption));
+
+ QmltcVisitor visitor(currentScope, &importer, &logger,
+ QQmlJSImportVisitor::implicitImportDirectory(url, &mapper), qmldirFiles);
+ visitor.setMode(QmltcVisitor::Compile);
+ QmltcTypeResolver typeResolver { &importer };
+ typeResolver.init(&visitor, qmlParser.rootNode());
+
+ using PassManagerPtr =
+ std::unique_ptr<QQmlSA::PassManager,
+ decltype(&QQmlSA::PassManagerPrivate::deletePassManager)>;
+ PassManagerPtr passMan(QQmlSA::PassManagerPrivate::createPassManager(&visitor, &typeResolver),
+ &QQmlSA::PassManagerPrivate::deletePassManager);
+ passMan->registerPropertyPass(std::make_unique<QQmlJSLiteralBindingCheck>(passMan.get()),
+ QString(), QString(), QString());
+ passMan->analyze(QQmlJSScope::createQQmlSAElement(visitor.result()));
+
+ if (logger.hasErrors())
+ return EXIT_FAILURE;
+
+ QList<QQmlJS::DiagnosticMessage> warnings = importer.takeGlobalWarnings();
+ if (!warnings.isEmpty()) {
+ logger.log(QStringLiteral("Type warnings occurred while compiling file:"), qmlImport,
+ QQmlJS::SourceLocation());
+ logger.processMessages(warnings, qmlImport);
+ // Log_Import is critical for the compiler
+ return EXIT_FAILURE;
+ }
+
+ QmltcCompiler compiler(url, &typeResolver, &visitor, &logger);
+ compiler.compile(info);
+
+ if (logger.hasErrors())
+ return EXIT_FAILURE;
+
+ return EXIT_SUCCESS;
+}
diff --git a/tools/qmltc/qmltccodewriter.cpp b/tools/qmltc/qmltccodewriter.cpp
new file mode 100644
index 0000000000..134de0f98e
--- /dev/null
+++ b/tools/qmltc/qmltccodewriter.cpp
@@ -0,0 +1,569 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "qmltccodewriter.h"
+
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qstringbuilder.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qmap.h>
+#include <QtCore/qlist.h>
+
+#include <utility>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+static QString urlToMacro(const QString &url)
+{
+ QFileInfo fi(url);
+ return u"Q_QMLTC_" + fi.baseName().toUpper();
+}
+
+static QString getFunctionCategory(const QmltcMethodBase &method)
+{
+ QString category;
+ switch (method.access) {
+ case QQmlJSMetaMethod::Private:
+ category = u"private"_s;
+ break;
+ case QQmlJSMetaMethod::Protected:
+ category = u"protected"_s;
+ break;
+ case QQmlJSMetaMethod::Public:
+ category = u"public"_s;
+ break;
+ }
+ return category;
+}
+
+static QString getFunctionCategory(const QmltcMethod &method)
+{
+ QString category = getFunctionCategory(static_cast<const QmltcMethodBase &>(method));
+ switch (method.type) {
+ case QQmlJSMetaMethodType::Signal:
+ category = u"Q_SIGNALS"_s;
+ break;
+ case QQmlJSMetaMethodType::Slot:
+ category += u" Q_SLOTS"_s;
+ break;
+ case QQmlJSMetaMethodType::Method:
+ case QQmlJSMetaMethodType::StaticMethod:
+ break;
+ }
+ return category;
+}
+
+static QString appendSpace(const QString &s)
+{
+ if (s.isEmpty())
+ return s;
+ return s + u" ";
+}
+
+static QString prependSpace(const QString &s)
+{
+ if (s.isEmpty())
+ return s;
+ return u" " + s;
+}
+
+static std::pair<QString, QString> functionSignatures(const QmltcMethodBase &method)
+{
+ const QString name = method.name;
+ const QList<QmltcVariable> &parameterList = method.parameterList;
+
+ QStringList headerParamList;
+ QStringList cppParamList;
+ for (const QmltcVariable &variable : parameterList) {
+ const QString commonPart = variable.cppType + u" " + variable.name;
+ cppParamList << commonPart;
+ headerParamList << commonPart;
+ if (!variable.defaultValue.isEmpty())
+ headerParamList.back() += u" = " + variable.defaultValue;
+ }
+
+ const QString headerSignature = name + u"(" + headerParamList.join(u", "_s) + u")"
+ + prependSpace(method.modifiers.join(u" "));
+ const QString cppSignature = name + u"(" + cppParamList.join(u", "_s) + u")"
+ + prependSpace(method.modifiers.join(u" "));
+ return { headerSignature, cppSignature };
+}
+
+static QString functionReturnType(const QmltcMethod &m)
+{
+ return appendSpace(m.declarationPrefixes.join(u" "_s)) + m.returnType;
+}
+
+void QmltcCodeWriter::writeGlobalHeader(QmltcOutputWrapper &code, const QString &sourcePath,
+ const QString &hPath, const QString &cppPath,
+ const QString &outNamespace,
+ const QSet<QString> &requiredCppIncludes)
+{
+ Q_UNUSED(cppPath);
+ const QString preamble = u"// This code is auto-generated by the qmltc tool from the file '"
+ + sourcePath + u"'\n// WARNING! All changes made in this file will be lost!\n";
+ code.rawAppendToHeader(preamble);
+ code.rawAppendToCpp(preamble);
+ code.rawAppendToHeader(
+ u"// NOTE: This generated API is to be considered implementation detail.");
+ code.rawAppendToHeader(
+ u"// It may change from version to version and should not be relied upon.");
+
+ const QString headerMacro = urlToMacro(sourcePath);
+ code.rawAppendToHeader(u"#ifndef %1_H"_s.arg(headerMacro));
+ code.rawAppendToHeader(u"#define %1_H"_s.arg(headerMacro));
+
+ code.rawAppendToHeader(u"#include <QtCore/qproperty.h>");
+ code.rawAppendToHeader(u"#include <QtCore/qobject.h>");
+ code.rawAppendToHeader(u"#include <QtCore/qcoreapplication.h>");
+ code.rawAppendToHeader(u"#include <QtCore/qxpfunctional.h>");
+ code.rawAppendToHeader(u"#include <QtQml/qqmlengine.h>");
+ code.rawAppendToHeader(u"#include <QtCore/qurl.h>"); // used in engine execution
+ code.rawAppendToHeader(u"#include <QtQml/qqml.h>"); // used for attached properties
+
+ code.rawAppendToHeader(u"#include <private/qqmlengine_p.h>"); // executeRuntimeFunction(), etc.
+ code.rawAppendToHeader(u"#include <private/qqmltcobjectcreationhelper_p.h>"); // QmltcSupportLib
+
+ code.rawAppendToHeader(u"#include <QtQml/qqmllist.h>"); // QQmlListProperty
+
+ // include custom C++ includes required by used types
+ code.rawAppendToHeader(u"// BEGIN(custom_cpp_includes)");
+ for (const auto &requiredInclude : requiredCppIncludes)
+ code.rawAppendToHeader(u"#include \"" + requiredInclude + u"\"");
+ code.rawAppendToHeader(u"// END(custom_cpp_includes)");
+
+ code.rawAppendToCpp(u"#include \"" + hPath + u"\""); // include own .h file
+ code.rawAppendToCpp(u"// qmltc support library:");
+ code.rawAppendToCpp(u"#include <private/qqmlcppbinding_p.h>"); // QmltcSupportLib
+ code.rawAppendToCpp(u"#include <private/qqmlcpponassignment_p.h>"); // QmltcSupportLib
+ code.rawAppendToHeader(u"#include <private/qqmlcpptypehelpers_p.h> "); // QmltcSupportLib
+
+ code.rawAppendToCpp(u"#include <private/qqmlobjectcreator_p.h>"); // createComponent()
+ code.rawAppendToCpp(u"#include <private/qqmlcomponent_p.h>"); // QQmlComponentPrivate::get()
+
+ code.rawAppendToCpp(u"");
+ code.rawAppendToCpp(u"#include <private/qobject_p.h>"); // NB: for private properties
+ code.rawAppendToCpp(u"#include <private/qqmlobjectcreator_p.h>"); // for finalize callbacks
+ code.rawAppendToCpp(u"#include <QtQml/qqmlprivate.h>"); // QQmlPrivate::qmlExtendedObject()
+
+ code.rawAppendToCpp(u""); // blank line
+ code.rawAppendToCpp(u"QT_USE_NAMESPACE // avoid issues with QT_NAMESPACE");
+
+ code.rawAppendToHeader(u""); // blank line
+
+ const QStringList namespaces = outNamespace.split(u"::"_s);
+
+ for (const QString &currentNamespace : namespaces) {
+ code.rawAppendToHeader(u"namespace %1 {"_s.arg(currentNamespace));
+ code.rawAppendToCpp(u"namespace %1 {"_s.arg(currentNamespace));
+ }
+}
+
+void QmltcCodeWriter::write(QmltcOutputWrapper &code,
+ const QmltcPropertyInitializer &propertyInitializer,
+ const QmltcType &wrappedType)
+{
+ code.rawAppendToHeader(u"class " + propertyInitializer.name + u" {");
+
+ {
+ {
+ [[maybe_unused]] QmltcOutputWrapper::HeaderIndentationScope headerIndent(&code);
+
+ code.rawAppendToHeader(u"friend class " + wrappedType.cppType + u";");
+ }
+
+ code.rawAppendToHeader(u"public:"_s);
+
+ [[maybe_unused]] QmltcOutputWrapper::MemberNameScope typeScope(&code, propertyInitializer.name);
+ {
+ [[maybe_unused]] QmltcOutputWrapper::HeaderIndentationScope headerIndent(&code);
+
+ write(code, propertyInitializer.constructor);
+ code.rawAppendToHeader(u""); // blank line
+
+ for (const auto &propertySetter : propertyInitializer.propertySetters) {
+ write(code, propertySetter);
+ }
+ }
+
+ code.rawAppendToHeader(u""); // blank line
+ code.rawAppendToHeader(u"private:"_s);
+
+ {
+ [[maybe_unused]] QmltcOutputWrapper::HeaderIndentationScope headerIndent(&code);
+
+ write(code, propertyInitializer.component);
+ write(code, propertyInitializer.initializedCache);
+ }
+ }
+
+ code.rawAppendToHeader(u"};"_s);
+ code.rawAppendToHeader(u""); // blank line
+}
+
+void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcRequiredPropertiesBundle &requiredPropertiesBundle)
+{
+ code.rawAppendToHeader(u"struct " + requiredPropertiesBundle.name + u" {");
+
+ {
+ [[maybe_unused]] QmltcOutputWrapper::HeaderIndentationScope headerIndent(&code);
+
+ for (const auto &member : requiredPropertiesBundle.members) {
+ write(code, member);
+ }
+ }
+
+ code.rawAppendToHeader(u"};"_s);
+ code.rawAppendToHeader(u""); // blank line
+}
+
+void QmltcCodeWriter::writeGlobalFooter(QmltcOutputWrapper &code, const QString &sourcePath,
+ const QString &outNamespace)
+{
+ const QStringList namespaces = outNamespace.split(u"::"_s);
+
+ for (auto it = namespaces.crbegin(), end = namespaces.crend(); it != end; it++) {
+ code.rawAppendToCpp(u"} // namespace %1"_s.arg(*it));
+ code.rawAppendToHeader(u"} // namespace %1"_s.arg(*it));
+ }
+
+ code.rawAppendToHeader(u""); // blank line
+ code.rawAppendToHeader(u"#endif // %1_H"_s.arg(urlToMacro(sourcePath)));
+ code.rawAppendToHeader(u""); // blank line
+}
+
+static void writeToFile(const QString &path, const QByteArray &data)
+{
+ // When not using dependency files, changing a single qml invalidates all
+ // qml files and would force the recompilation of everything. To avoid that,
+ // we check if the data is equal to the existing file, if yes, don't touch
+ // it so the build system will not recompile unnecessary things.
+ //
+ // If the build system use dependency file, we should anyway touch the file
+ // so qmltc is not re-run
+ QFileInfo fi(path);
+ if (fi.exists() && fi.size() == data.size()) {
+ QFile oldFile(path);
+ if (oldFile.open(QIODevice::ReadOnly)) {
+ if (oldFile.readAll() == data)
+ return;
+ }
+ }
+ QFile file(path);
+ if (!file.open(QIODevice::WriteOnly))
+ qFatal("Could not open file %s", qPrintable(path));
+ file.write(data);
+}
+
+void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcProgram &program)
+{
+ writeGlobalHeader(code, program.url, program.hPath, program.cppPath, program.outNamespace,
+ program.includes);
+
+ // url method comes first
+ writeUrl(code, program.urlMethod);
+
+ // forward declare all the types first
+ for (const QmltcType &type : std::as_const(program.compiledTypes))
+ code.rawAppendToHeader(u"class " + type.cppType + u";");
+ // write all the types and their content
+ for (const QmltcType &type : std::as_const(program.compiledTypes))
+ write(code, type, program.exportMacro);
+
+ // add typeCount definitions. after all types have been written down (so
+ // they are now complete types as per C++). practically, this only concerns
+ // document root type
+ for (const QmltcType &type : std::as_const(program.compiledTypes)) {
+ if (!type.typeCount)
+ continue;
+ code.rawAppendToHeader(u""); // blank line
+ code.rawAppendToHeader(u"constexpr %1 %2::%3()"_s.arg(type.typeCount->returnType,
+ type.cppType, type.typeCount->name));
+ code.rawAppendToHeader(u"{");
+ for (const QString &line : std::as_const(type.typeCount->body))
+ code.rawAppendToHeader(line, 1);
+ code.rawAppendToHeader(u"}");
+ }
+
+ writeGlobalFooter(code, program.url, program.outNamespace);
+
+ writeToFile(program.hPath, code.code().header.toUtf8());
+ writeToFile(program.cppPath, code.code().cpp.toUtf8());
+}
+
+template<typename Predicate>
+static void dumpFunctions(QmltcOutputWrapper &code, const QList<QmltcMethod> &functions,
+ Predicate pred)
+{
+ // functions are _ordered_ by access and kind. ordering is important to
+ // provide consistent output
+ QMap<QString, QList<const QmltcMethod *>> orderedFunctions;
+ for (const auto &function : functions) {
+ if (pred(function))
+ orderedFunctions[getFunctionCategory(function)].append(std::addressof(function));
+ }
+
+ for (auto it = orderedFunctions.cbegin(); it != orderedFunctions.cend(); ++it) {
+ code.rawAppendToHeader(it.key() + u":", -1);
+ for (const QmltcMethod *function : std::as_const(it.value()))
+ QmltcCodeWriter::write(code, *function);
+ }
+}
+
+void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcType &type,
+ const QString &exportMacro)
+{
+ const auto constructClassString = [&]() {
+ QString str = u"class "_s;
+ if (!exportMacro.isEmpty())
+ str.append(exportMacro).append(u" "_s);
+ str.append(type.cppType);
+ QStringList nonEmptyBaseClasses;
+ nonEmptyBaseClasses.reserve(type.baseClasses.size());
+ std::copy_if(type.baseClasses.cbegin(), type.baseClasses.cend(),
+ std::back_inserter(nonEmptyBaseClasses),
+ [](const QString &entry) { return !entry.isEmpty(); });
+ if (!nonEmptyBaseClasses.isEmpty())
+ str += u" : public " + nonEmptyBaseClasses.join(u", public "_s);
+ return str;
+ };
+
+ code.rawAppendToHeader(u""); // blank line
+ code.rawAppendToCpp(u""); // blank line
+
+ code.rawAppendToHeader(constructClassString());
+ code.rawAppendToHeader(u"{");
+ for (const QString &mocLine : std::as_const(type.mocCode))
+ code.rawAppendToHeader(mocLine, 1);
+
+ QmltcOutputWrapper::MemberNameScope typeScope(&code, type.cppType);
+ Q_UNUSED(typeScope);
+ {
+ QmltcOutputWrapper::HeaderIndentationScope headerIndent(&code);
+ Q_UNUSED(headerIndent);
+
+ // first, write user-visible code, then everything else. someone might
+ // want to look at the generated code, so let's make an effort when
+ // writing it down
+
+ code.rawAppendToHeader(u"/* ----------------- */");
+ code.rawAppendToHeader(u"/* External C++ API */");
+ code.rawAppendToHeader(u"public:", -1);
+
+ if (!type.propertyInitializer.name.isEmpty())
+ write(code, type.propertyInitializer, type);
+
+ if (type.requiredPropertiesBundle)
+ write(code, *type.requiredPropertiesBundle);
+
+ // NB: when non-document root, the externalCtor won't be public - but we
+ // really don't care about the output format of such types
+ if (!type.ignoreInit && type.externalCtor.access == QQmlJSMetaMethod::Public) {
+ // TODO: ignoreInit must be eliminated
+
+ QmltcCodeWriter::write(code, type.externalCtor);
+ if (type.staticCreate)
+ QmltcCodeWriter::write(code, *type.staticCreate);
+ }
+
+ // dtor
+ if (type.dtor)
+ QmltcCodeWriter::write(code, *type.dtor);
+
+ // enums
+ for (const auto &enumeration : std::as_const(type.enums))
+ QmltcCodeWriter::write(code, enumeration);
+
+ // visible functions
+ const auto isUserVisibleFunction = [](const QmltcMethod &function) {
+ return function.userVisible;
+ };
+ dumpFunctions(code, type.functions, isUserVisibleFunction);
+
+ code.rawAppendToHeader(u"/* ----------------- */");
+ code.rawAppendToHeader(u""); // blank line
+ code.rawAppendToHeader(u"/* Internal functionality (do NOT use it!) */");
+
+ // below are the hidden parts of the type
+
+ // (rest of the) ctors
+ if (type.ignoreInit) { // TODO: this branch should be eliminated
+ Q_ASSERT(type.baselineCtor.access == QQmlJSMetaMethod::Public);
+ code.rawAppendToHeader(u"public:", -1);
+ QmltcCodeWriter::write(code, type.baselineCtor);
+ } else {
+ code.rawAppendToHeader(u"protected:", -1);
+ if (type.externalCtor.access != QQmlJSMetaMethod::Public) {
+ Q_ASSERT(type.externalCtor.access == QQmlJSMetaMethod::Protected);
+ QmltcCodeWriter::write(code, type.externalCtor);
+ }
+ QmltcCodeWriter::write(code, type.baselineCtor);
+ QmltcCodeWriter::write(code, type.init);
+ QmltcCodeWriter::write(code, type.endInit);
+ QmltcCodeWriter::write(code, type.setComplexBindings);
+ QmltcCodeWriter::write(code, type.beginClass);
+ QmltcCodeWriter::write(code, type.completeComponent);
+ QmltcCodeWriter::write(code, type.finalizeComponent);
+ QmltcCodeWriter::write(code, type.handleOnCompleted);
+ }
+
+ // children
+ for (const auto &child : std::as_const(type.children))
+ QmltcCodeWriter::write(code, child, exportMacro);
+
+ // (non-visible) functions
+ dumpFunctions(code, type.functions, std::not_fn(isUserVisibleFunction));
+
+ // variables and properties
+ if (!type.variables.isEmpty() || !type.properties.isEmpty()) {
+ code.rawAppendToHeader(u""); // blank line
+ code.rawAppendToHeader(u"protected:", -1);
+ }
+ for (const auto &property : std::as_const(type.properties))
+ write(code, property);
+ for (const auto &variable : std::as_const(type.variables))
+ write(code, variable);
+ }
+
+ code.rawAppendToHeader(u"private:", -1);
+ for (const QString &otherLine : std::as_const(type.otherCode))
+ code.rawAppendToHeader(otherLine, 1);
+
+ if (type.typeCount) {
+ // add typeCount declaration, definition is added later
+ code.rawAppendToHeader(u""); // blank line
+ code.rawAppendToHeader(u"protected:");
+ code.rawAppendToHeader(u"constexpr static %1 %2();"_s.arg(type.typeCount->returnType,
+ type.typeCount->name),
+ 1);
+ }
+
+ code.rawAppendToHeader(u"};");
+}
+
+void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcEnum &enumeration)
+{
+ code.rawAppendToHeader(u"enum " + enumeration.cppType + u" {");
+ for (qsizetype i = 0; i < enumeration.keys.size(); ++i) {
+ QString str;
+ if (enumeration.values.isEmpty()) {
+ str += enumeration.keys.at(i) + u",";
+ } else {
+ str += enumeration.keys.at(i) + u" = " + enumeration.values.at(i) + u",";
+ }
+ code.rawAppendToHeader(str, 1);
+ }
+ code.rawAppendToHeader(u"};");
+ code.rawAppendToHeader(enumeration.ownMocLine);
+}
+
+void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcMethod &method)
+{
+ const auto [hSignature, cppSignature] = functionSignatures(method);
+ // Note: augment return type with preambles in declaration
+ code.rawAppendToHeader((method.type == QQmlJSMetaMethodType::StaticMethod
+ ? u"static " + functionReturnType(method)
+ : functionReturnType(method))
+ + u" " + hSignature + u";");
+
+ // do not generate method implementation if it is a signal
+ const auto methodType = method.type;
+ if (methodType != QQmlJSMetaMethodType::Signal) {
+ code.rawAppendToCpp(u""_s); // blank line
+ if (method.comments.size() > 0) {
+ code.rawAppendToCpp(u"/*! \\internal"_s);
+ for (const auto &comment : method.comments)
+ code.rawAppendToCpp(comment, 1);
+ code.rawAppendToCpp(u"*/"_s);
+ }
+ code.rawAppendToCpp(method.returnType);
+ code.rawAppendSignatureToCpp(cppSignature);
+ code.rawAppendToCpp(u"{");
+ {
+ QmltcOutputWrapper::CppIndentationScope cppIndent(&code);
+ Q_UNUSED(cppIndent);
+ for (const QString &line : std::as_const(method.body))
+ code.rawAppendToCpp(line);
+ }
+ code.rawAppendToCpp(u"}");
+ }
+}
+
+template<typename WriteInitialization>
+static void writeSpecialMethod(QmltcOutputWrapper &code, const QmltcMethodBase &specialMethod,
+ WriteInitialization writeInit)
+{
+ const auto [hSignature, cppSignature] = functionSignatures(specialMethod);
+ code.rawAppendToHeader(hSignature + u";");
+
+ code.rawAppendToCpp(u""); // blank line
+ code.rawAppendSignatureToCpp(cppSignature);
+
+ writeInit(specialMethod);
+
+ code.rawAppendToCpp(u"{");
+ {
+ QmltcOutputWrapper::CppIndentationScope cppIndent(&code);
+ Q_UNUSED(cppIndent);
+ for (const QString &line : std::as_const(specialMethod.body))
+ code.rawAppendToCpp(line);
+ }
+ code.rawAppendToCpp(u"}");
+}
+
+void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcCtor &ctor)
+{
+ const auto writeInitializerList = [&](const QmltcMethodBase &ctorBase) {
+ auto ctor = static_cast<const QmltcCtor &>(ctorBase);
+ if (!ctor.initializerList.isEmpty()) {
+ code.rawAppendToCpp(u":", 1);
+ // double \n to make separate initializer list lines stand out more
+ code.rawAppendToCpp(
+ ctor.initializerList.join(u",\n\n" + u" "_s.repeated(code.cppIndent + 1)),
+ 1);
+ }
+ };
+
+ writeSpecialMethod(code, ctor, writeInitializerList);
+}
+
+void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcDtor &dtor)
+{
+ const auto noop = [](const QmltcMethodBase &) {};
+ writeSpecialMethod(code, dtor, noop);
+}
+
+void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcVariable &var)
+{
+ const QString optionalPart = var.defaultValue.isEmpty() ? u""_s : u" = " + var.defaultValue;
+ code.rawAppendToHeader(var.cppType + u" " + var.name + optionalPart + u";");
+}
+
+void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcProperty &prop)
+{
+ Q_ASSERT(prop.defaultValue.isEmpty()); // we don't support it yet (or at all?)
+ code.rawAppendToHeader(u"Q_OBJECT_BINDABLE_PROPERTY(%1, %2, %3, &%1::%4)"_s.arg(
+ prop.containingClass, prop.cppType, prop.name, prop.signalName));
+}
+
+void QmltcCodeWriter::writeUrl(QmltcOutputWrapper &code, const QmltcMethod &urlMethod)
+{
+ // unlike ordinary methods, url function only exists in .cpp
+ Q_ASSERT(!urlMethod.returnType.isEmpty());
+ const auto [hSignature, _] = functionSignatures(urlMethod);
+ Q_UNUSED(_);
+ // Note: augment return type with preambles in declaration
+ code.rawAppendToCpp(functionReturnType(urlMethod) + u" " + hSignature);
+ code.rawAppendToCpp(u"{");
+ {
+ QmltcOutputWrapper::CppIndentationScope cppIndent(&code);
+ Q_UNUSED(cppIndent);
+ for (const QString &line : std::as_const(urlMethod.body))
+ code.rawAppendToCpp(line);
+ }
+ code.rawAppendToCpp(u"}");
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qmltc/qmltccodewriter.h b/tools/qmltc/qmltccodewriter.h
new file mode 100644
index 0000000000..20b0262737
--- /dev/null
+++ b/tools/qmltc/qmltccodewriter.h
@@ -0,0 +1,39 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef QMLTCCODEWRITER_H
+#define QMLTCCODEWRITER_H
+
+#include "qmltcoutputprimitives.h"
+#include "qmltcoutputir.h"
+
+#include <QtCore/qstring.h>
+
+QT_BEGIN_NAMESPACE
+
+struct QmltcCodeWriter
+{
+ static void writeGlobalHeader(QmltcOutputWrapper &code, const QString &sourcePath,
+ const QString &hPath, const QString &cppPath,
+ const QString &outNamespace,
+ const QSet<QString> &requiredCppIncludes);
+ static void writeGlobalFooter(QmltcOutputWrapper &code, const QString &sourcePath,
+ const QString &outNamespace);
+ static void write(QmltcOutputWrapper &code, const QmltcProgram &program);
+ static void write(QmltcOutputWrapper &code, const QmltcType &type, const QString &exportMacro);
+ static void write(QmltcOutputWrapper &code, const QmltcEnum &enumeration);
+ static void write(QmltcOutputWrapper &code, const QmltcMethod &method);
+ static void write(QmltcOutputWrapper &code, const QmltcCtor &ctor);
+ static void write(QmltcOutputWrapper &code, const QmltcDtor &dtor);
+ static void write(QmltcOutputWrapper &code, const QmltcVariable &var);
+ static void write(QmltcOutputWrapper &code, const QmltcProperty &prop);
+ static void write(QmltcOutputWrapper &code, const QmltcPropertyInitializer &propertyInitializer, const QmltcType& wrappedType);
+ static void write(QmltcOutputWrapper &code, const QmltcRequiredPropertiesBundle &requiredPropertiesBundle);
+
+private:
+ static void writeUrl(QmltcOutputWrapper &code, const QmltcMethod &urlMethod); // special
+};
+
+QT_END_NAMESPACE
+
+#endif // QMLTCCODEWRITER_H
diff --git a/tools/qmltc/qmltccommandlineutils.cpp b/tools/qmltc/qmltccommandlineutils.cpp
new file mode 100644
index 0000000000..e3f6b4d3b7
--- /dev/null
+++ b/tools/qmltc/qmltccommandlineutils.cpp
@@ -0,0 +1,69 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "qmltccommandlineutils.h"
+
+#include <QtCore/qstring.h>
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qdir.h>
+#include <QtCore/qurl.h>
+#include <QtCore/qlibraryinfo.h>
+
+QT_BEGIN_NAMESPACE
+
+QString parseUrlArgument(const QString &arg)
+{
+ const QUrl url = QUrl::fromUserInput(arg, QDir::currentPath(), QUrl::AssumeLocalFile);
+ if (!url.isValid()) {
+ fprintf(stderr, "Invalid URL: \"%s\"\n", qPrintable(arg));
+ return QString();
+ }
+ if (!url.isLocalFile()) {
+ fprintf(stderr, "\"%s\" is not a local file\n", qPrintable(arg));
+ return QString();
+ }
+ return url.toLocalFile();
+}
+
+QString loadUrl(const QString &url)
+{
+ const QFileInfo fi(url);
+ if (!fi.exists()) {
+ fprintf(stderr, "\"%s\" does not exist.\n",
+ qPrintable(QDir::toNativeSeparators(fi.absoluteFilePath())));
+ return QString();
+ }
+
+ QFile f(fi.absoluteFilePath());
+ if (!f.open(QIODevice::ReadOnly)) {
+ fprintf(stderr, "Unable to read \"%s\": %s.\n",
+ qPrintable(QDir::toNativeSeparators(fi.absoluteFilePath())),
+ qPrintable(f.errorString()));
+ return QString();
+ }
+
+ QByteArray data(fi.size(), Qt::Uninitialized);
+ if (f.read(data.data(), data.size()) != data.size()) {
+ fprintf(stderr, "Unable to read \"%s\": %s.\n",
+ qPrintable(QDir::toNativeSeparators(fi.absoluteFilePath())),
+ qPrintable(f.errorString()));
+ return QString();
+ }
+ return QString::fromUtf8(data);
+}
+
+QString getImplicitImportDirectory(const QString &url)
+{
+ const QFileInfo fi(url);
+ Q_ASSERT(fi.exists());
+ QDir dir = fi.dir();
+ QString implicitImport = dir.canonicalPath(); // resolves symlinks, etc.
+ if (implicitImport.isEmpty()) {
+ fprintf(stderr, "Cannot resolve implicit import directory of \"%s\"",
+ qPrintable(QDir::toNativeSeparators(fi.absoluteFilePath())));
+ return QString();
+ }
+ return implicitImport;
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qmltc/qmltccommandlineutils.h b/tools/qmltc/qmltccommandlineutils.h
new file mode 100644
index 0000000000..8ad3232d1f
--- /dev/null
+++ b/tools/qmltc/qmltccommandlineutils.h
@@ -0,0 +1,17 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef QMLTCCOMMANDLINEUTILS_H
+#define QMLTCCOMMANDLINEUTILS_H
+
+#include <QtCore/qstring.h>
+
+QT_BEGIN_NAMESPACE
+
+QString parseUrlArgument(const QString &arg);
+QString loadUrl(const QString &url);
+QString getImplicitImportDirectory(const QString &url);
+
+QT_END_NAMESPACE
+
+#endif // QMLTCCOMMANDLINEUTILS_H
diff --git a/tools/qmltc/qmltccompiler.cpp b/tools/qmltc/qmltccompiler.cpp
new file mode 100644
index 0000000000..75bd580e07
--- /dev/null
+++ b/tools/qmltc/qmltccompiler.cpp
@@ -0,0 +1,2053 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "qmltccompiler.h"
+#include "qmltcoutputir.h"
+#include "qmltccodewriter.h"
+#include "qmltcpropertyutils.h"
+#include "qmltccompilerpieces.h"
+
+#include <QtCore/qloggingcategory.h>
+#include <QtQml/private/qqmlsignalnames_p.h>
+#include <private/qqmljsutils_p.h>
+
+#include <algorithm>
+
+QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
+bool qIsReferenceTypeList(const QQmlJSMetaProperty &p)
+{
+ if (QQmlJSScope::ConstPtr type = p.type())
+ return type->isListProperty();
+ return false;
+}
+
+static QList<QQmlJSMetaProperty> unboundRequiredProperties(
+ const QQmlJSScope::ConstPtr &type,
+ QmltcTypeResolver *resolver
+) {
+ QList<QQmlJSMetaProperty> requiredProperties{};
+
+ auto isPropertyRequired = [&type, &resolver](const auto &property) {
+ if (!type->isPropertyRequired(property.propertyName()))
+ return false;
+
+ if (type->hasPropertyBindings(property.propertyName()))
+ return false;
+
+ if (property.isAlias()) {
+ QQmlJSUtils::AliasResolutionVisitor aliasVisitor;
+
+ QQmlJSUtils::ResolvedAlias result =
+ QQmlJSUtils::resolveAlias(resolver, property, type, aliasVisitor);
+
+ if (result.kind != QQmlJSUtils::AliasTarget_Property)
+ return false;
+
+ // If the top level alias targets a property that is in
+ // the top level scope and that property is required, then
+ // we will already pick up the property during one of the
+ // iterations.
+ // Setting the property or the alias is the same so we
+ // discard one of the two, as otherwise we would require
+ // the user to pass two values for the same property ,in
+ // this case the alias.
+ //
+ // For example in:
+ //
+ // ```
+ // Item {
+ // id: self
+ // required property int foo
+ // property alias bar: self.foo
+ // }
+ // ```
+ //
+ // Both foo and bar are required but setting one or the
+ // other is the same operation so that we should choose
+ // only one.
+ if (result.owner == type &&
+ type->isPropertyRequired(result.property.propertyName()))
+ return false;
+
+ if (result.owner->hasPropertyBindings(result.property.propertyName()))
+ return false;
+ }
+
+ return true;
+ };
+
+ const auto properties = type->properties();
+ std::copy_if(properties.cbegin(), properties.cend(),
+ std::back_inserter(requiredProperties), isPropertyRequired);
+ std::sort(requiredProperties.begin(), requiredProperties.end(),
+ [](const auto &left, const auto &right) {
+ return left.propertyName() < right.propertyName();
+ });
+
+ return requiredProperties;
+}
+
+
+// Populates the internal representation for a
+// RequiredPropertiesBundle, a class that acts as a bundle of initial
+// values that should be set for the required properties of a type.
+static void compileRequiredPropertiesBundle(
+ QmltcType &current,
+ const QQmlJSScope::ConstPtr &type,
+ QmltcTypeResolver *resolver
+) {
+
+ QList<QQmlJSMetaProperty> requiredProperties = unboundRequiredProperties(type, resolver);
+
+ if (requiredProperties.isEmpty())
+ return;
+
+ current.requiredPropertiesBundle.emplace();
+ current.requiredPropertiesBundle->name = u"RequiredPropertiesBundle"_s;
+
+ current.requiredPropertiesBundle->members.reserve(requiredProperties.size());
+ std::transform(requiredProperties.cbegin(), requiredProperties.cend(),
+ std::back_inserter(current.requiredPropertiesBundle->members),
+ [](const QQmlJSMetaProperty &property) {
+ QString type = qIsReferenceTypeList(property)
+ ? u"const QList<%1*>&"_s.arg(
+ property.type()->valueType()->internalName())
+ : u"passByConstRefOrValue<%1>"_s.arg(
+ property.type()->augmentedInternalName());
+ return QmltcVariable{ type, property.propertyName() };
+ });
+}
+
+static void compileRootExternalConstructorBody(
+ QmltcType& current,
+ const QQmlJSScope::ConstPtr &type
+) {
+ current.externalCtor.body << u"// document root:"_s;
+ // if it's document root, we want to create our QQmltcObjectCreationBase
+ // that would store all the created objects
+ current.externalCtor.body << u"QQmltcObjectCreationBase<%1> objectHolder;"_s.arg(
+ type->internalName());
+ current.externalCtor.body
+ << u"QQmltcObjectCreationHelper creator = objectHolder.view();"_s;
+ current.externalCtor.body << u"creator.set(0, this);"_s; // special case
+
+ QString initializerName = u"initializer"_s;
+ if (current.requiredPropertiesBundle) {
+ // Compose new initializer based on the initial values for required properties.
+ current.externalCtor.body << u"auto newInitializer = [&](auto& propertyInitializer) {"_s;
+ for (const auto& member : current.requiredPropertiesBundle->members) {
+ current.externalCtor.body << u" propertyInitializer.%1(requiredPropertiesBundle.%2);"_s.arg(
+ QmltcPropertyData(member.name).write, member.name
+ );
+ }
+ current.externalCtor.body << u" initializer(propertyInitializer);"_s;
+ current.externalCtor.body << u"};"_s;
+
+ initializerName = u"newInitializer"_s;
+ }
+
+ // now call init
+ current.externalCtor.body << current.init.name
+ + u"(&creator, engine, QQmlContextData::get(engine->rootContext()), /* "
+ u"endInit */ true, %1);"_s.arg(initializerName);
+};
+
+Q_LOGGING_CATEGORY(lcQmltcCompiler, "qml.qmltc.compiler", QtWarningMsg);
+
+const QString QmltcCodeGenerator::privateEngineName = u"ePriv"_s;
+const QString QmltcCodeGenerator::typeCountName = u"q_qmltc_typeCount"_s;
+
+QmltcCompiler::QmltcCompiler(const QString &url, QmltcTypeResolver *resolver, QmltcVisitor *visitor,
+ QQmlJSLogger *logger)
+ : m_url(url), m_typeResolver(resolver), m_visitor(visitor), m_logger(logger)
+{
+ Q_UNUSED(m_typeResolver);
+ Q_ASSERT(!hasErrors());
+}
+
+// needed due to std::unique_ptr<CodeGenerator> with CodeGenerator being
+// incomplete type in the header (~std::unique_ptr<> fails with a static_assert)
+QmltcCompiler::~QmltcCompiler() = default;
+
+QString QmltcCompiler::newSymbol(const QString &base)
+{
+ QString symbol = base;
+ symbol.replace(QLatin1String("."), QLatin1String("_"));
+ while (symbol.startsWith(QLatin1Char('_')) && symbol.size() >= 2
+ && (symbol[1].isUpper() || symbol[1] == QLatin1Char('_'))) {
+ symbol.remove(0, 1);
+ }
+ if (!m_symbols.contains(symbol)) {
+ m_symbols.insert(symbol, 1);
+ } else {
+ symbol += u"_" + QString::number(m_symbols[symbol]++);
+ }
+ return symbol;
+}
+
+void QmltcCompiler::compile(const QmltcCompilerInfo &info)
+{
+ m_info = info;
+ Q_ASSERT(!m_info.outputCppFile.isEmpty());
+ Q_ASSERT(!m_info.outputHFile.isEmpty());
+ Q_ASSERT(!m_info.resourcePath.isEmpty());
+
+ // Note: we only compile "pure" QML types. any component-wrapped type is
+ // expected to appear through a binding
+
+ const auto isComponent = [](const QQmlJSScope::ConstPtr &type) {
+ auto base = type->baseType();
+ return base && base->internalName() == u"QQmlComponent"_s;
+ };
+
+ QmltcCodeGenerator generator { m_url, m_visitor };
+
+ QmltcMethod urlMethod;
+ compileUrlMethod(urlMethod, generator.urlMethodName());
+ m_urlMethodName = urlMethod.name;
+
+ // sort inline components to compile them in the right order
+ // a inherits b => b needs to be defined in the cpp file before a!
+ // r is the root => r needs to be compiled at the end!
+ // otherwise => sort them by inline component names to have consistent output
+ auto sortedInlineComponentNames = m_visitor->inlineComponentNames();
+ std::sort(sortedInlineComponentNames.begin(), sortedInlineComponentNames.end(),
+ [&](const InlineComponentOrDocumentRootName &a,
+ const InlineComponentOrDocumentRootName &b) {
+ const auto *inlineComponentAName = std::get_if<InlineComponentNameType>(&a);
+ const auto *inlineComponentBName = std::get_if<InlineComponentNameType>(&b);
+
+ if (inlineComponentAName == inlineComponentBName)
+ return false;
+
+ // the root comes at last, so (a < b) == true when b is the root and a is not
+ if (inlineComponentAName && !inlineComponentBName)
+ return true;
+
+ // b requires a to be declared before b when b inherits from a, therefore (a < b)
+ // == true
+ if (inlineComponentAName && inlineComponentBName) {
+ QQmlJSScope::ConstPtr inlineComponentA = m_visitor->inlineComponent(a);
+ QQmlJSScope::ConstPtr inlineComponentB = m_visitor->inlineComponent(b);
+ if (inlineComponentB->inherits(inlineComponentA)) {
+ return true;
+ } else if (inlineComponentA->inherits(inlineComponentB)) {
+ return false;
+ } else {
+ // fallback to default sorting based on names
+ return *inlineComponentAName < *inlineComponentBName;
+ }
+ }
+ Q_ASSERT(!inlineComponentAName || !inlineComponentBName);
+ // a is the root or both a and b are the root
+ return false;
+ });
+
+ QList<QmltcType> compiledTypes;
+ for (const auto &inlineComponent : sortedInlineComponentNames) {
+ const QList<QQmlJSScope::ConstPtr> &pureTypes = m_visitor->pureQmlTypes(inlineComponent);
+ Q_ASSERT(!pureTypes.empty());
+ const QQmlJSScope::ConstPtr &root = pureTypes.front();
+ if (isComponent(root)) {
+ compiledTypes.emplaceBack(); // create empty type
+ const auto compile = [&](QmltcType &current, const QQmlJSScope::ConstPtr &type) {
+ generator.generate_initCodeForTopLevelComponent(current, type);
+ };
+ compileType(compiledTypes.back(), root, compile);
+ } else {
+ const auto compile = [this](QmltcType &current, const QQmlJSScope::ConstPtr &type) {
+ compileTypeElements(current, type);
+ };
+
+ for (const auto &type : pureTypes) {
+ Q_ASSERT(type->scopeType() == QQmlSA::ScopeType::QMLScope);
+ compiledTypes.emplaceBack(); // create empty type
+ compileType(compiledTypes.back(), type, compile);
+ }
+ }
+ }
+
+ if (hasErrors())
+ return;
+
+ QmltcProgram program;
+ program.url = m_url;
+ program.cppPath = m_info.outputCppFile;
+ program.hPath = m_info.outputHFile;
+ program.outNamespace = m_info.outputNamespace;
+ program.exportMacro = m_info.exportMacro;
+ program.compiledTypes = compiledTypes;
+ program.includes = m_visitor->cppIncludeFiles();
+ if (!m_info.exportMacro.isEmpty() && !m_info.exportInclude.isEmpty())
+ program.includes += (m_info.exportInclude);
+ program.urlMethod = urlMethod;
+
+ QmltcOutput out;
+ QmltcOutputWrapper code(out);
+ QmltcCodeWriter::write(code, program);
+}
+
+void QmltcCompiler::compileUrlMethod(QmltcMethod &urlMethod, const QString &urlMethodName)
+{
+ urlMethod.name = urlMethodName;
+ urlMethod.returnType = u"const QUrl&"_s;
+ urlMethod.body << u"static QUrl url {QStringLiteral(\"qrc:%1\")};"_s.arg(m_info.resourcePath);
+ urlMethod.body << u"return url;"_s;
+ urlMethod.declarationPrefixes << u"static"_s;
+ urlMethod.modifiers << u"noexcept"_s;
+}
+
+void QmltcCompiler::compileType(
+ QmltcType &current, const QQmlJSScope::ConstPtr &type,
+ std::function<void(QmltcType &, const QQmlJSScope::ConstPtr &)> compileElements)
+{
+ Q_ASSERT(!type->internalName().isEmpty());
+ current.cppType = type->internalName();
+ Q_ASSERT(!type->baseType()->internalName().isEmpty());
+ const QString baseClass = type->baseType()->internalName();
+
+ const auto rootType = m_visitor->result();
+ const InlineComponentOrDocumentRootName name = type->enclosingInlineComponentName();
+ QQmlJSScope::ConstPtr inlineComponentType = m_visitor->inlineComponent(name);
+ Q_ASSERT(inlineComponentType);
+ const bool documentRoot = (type == rootType);
+ const bool inlineComponent = type->isInlineComponent();
+ const bool isAnonymous = !documentRoot || type->internalName().at(0).isLower();
+ const bool isSingleton = type->isSingleton();
+
+ QmltcCodeGenerator generator { m_url, m_visitor };
+
+ current.baseClasses = { baseClass };
+ if (!documentRoot) {
+ // make document root a friend to allow it to access init and endInit
+ const QString rootInternalName =
+ m_visitor->inlineComponent(type->enclosingInlineComponentName())->internalName();
+ if (rootInternalName != current.cppType) // avoid GCC13 warning on self-befriending
+ current.otherCode << "friend class %1;"_L1.arg(rootInternalName);
+ }
+ if (documentRoot || inlineComponent) {
+ auto name = type->inlineComponentName()
+ ? InlineComponentOrDocumentRootName(*type->inlineComponentName())
+ : InlineComponentOrDocumentRootName(RootDocumentNameType());
+ // make QQmltcObjectCreationBase<DocumentRoot> a friend to allow it to
+ // be created for the root object
+ current.otherCode << u"friend class QQmltcObjectCreationBase<%1>;"_s.arg(
+ inlineComponentType->internalName());
+ // generate typeCount for all components (root + inlineComponents)
+ QmltcMethod typeCountMethod;
+ typeCountMethod.name = QmltcCodeGenerator::typeCountName;
+ typeCountMethod.returnType = u"uint"_s;
+ typeCountMethod.body << u"return " + generator.generate_typeCount(name) + u";";
+ current.typeCount = typeCountMethod;
+ } else {
+ // make an immediate parent a friend since that parent
+ // would create the object through a non-public constructor
+ const auto realQmlScope = [](const QQmlJSScope::ConstPtr &scope) {
+ if (scope->isArrayScope())
+ return scope->parentScope();
+ return scope;
+ };
+
+ const auto& realScope = realQmlScope(type->parentScope());
+ if (realScope != rootType) {
+ current.otherCode << u"friend class %1;"_s.arg(realScope->internalName());
+ }
+ }
+
+ // make QQmltcObjectCreationHelper a friend of every type since it provides
+ // useful helper methods for all types
+ current.otherCode << u"friend class QT_PREPEND_NAMESPACE(QQmltcObjectCreationHelper);"_s;
+
+ current.mocCode = {
+ u"Q_OBJECT"_s,
+ // Note: isAnonymous holds for non-root types in the document as well
+ type->isInlineComponent() ? (u"QML_NAMED_ELEMENT(%1)"_s.arg(*type->inlineComponentName()))
+ : (isAnonymous ? u"QML_ANONYMOUS"_s : u"QML_ELEMENT"_s),
+ };
+
+ // add special member functions
+ current.baselineCtor.access = QQmlJSMetaMethod::Protected;
+ if (documentRoot || inlineComponent || isSingleton) {
+ current.externalCtor.access = QQmlJSMetaMethod::Public;
+ } else {
+ current.externalCtor.access = QQmlJSMetaMethod::Protected;
+ }
+ current.init.access = QQmlJSMetaMethod::Protected;
+ current.beginClass.access = QQmlJSMetaMethod::Protected;
+ current.endInit.access = QQmlJSMetaMethod::Protected;
+ current.setComplexBindings.access = QQmlJSMetaMethod::Protected;
+ current.completeComponent.access = QQmlJSMetaMethod::Protected;
+ current.finalizeComponent.access = QQmlJSMetaMethod::Protected;
+ current.handleOnCompleted.access = QQmlJSMetaMethod::Protected;
+
+ current.propertyInitializer.name = u"PropertyInitializer"_s;
+ current.propertyInitializer.constructor.access = QQmlJSMetaMethod::Public;
+ current.propertyInitializer.constructor.name = current.propertyInitializer.name;
+ current.propertyInitializer.constructor.parameterList = {
+ QmltcVariable(u"%1&"_s.arg(current.cppType), u"component"_s)
+ };
+ current.propertyInitializer.component.cppType = current.cppType + u"&";
+ current.propertyInitializer.component.name = u"component"_s;
+ current.propertyInitializer.initializedCache.cppType = u"QSet<QString>"_s;
+ current.propertyInitializer.initializedCache.name = u"initializedCache"_s;
+
+ current.baselineCtor.name = current.cppType;
+ current.externalCtor.name = current.cppType;
+ current.init.name = u"QML_init"_s;
+ current.init.returnType = u"QQmlRefPointer<QQmlContextData>"_s;
+ current.beginClass.name = u"QML_beginClass"_s;
+ current.beginClass.returnType = u"void"_s;
+ current.endInit.name = u"QML_endInit"_s;
+ current.endInit.returnType = u"void"_s;
+ current.setComplexBindings.name = u"QML_setComplexBindings"_s;
+ current.setComplexBindings.returnType = u"void"_s;
+ current.completeComponent.name = u"QML_completeComponent"_s;
+ current.completeComponent.returnType = u"void"_s;
+ current.finalizeComponent.name = u"QML_finalizeComponent"_s;
+ current.finalizeComponent.returnType = u"void"_s;
+ current.handleOnCompleted.name = u"QML_handleOnCompleted"_s;
+ current.handleOnCompleted.returnType = u"void"_s;
+ QmltcVariable creator(u"QQmltcObjectCreationHelper*"_s, u"creator"_s);
+ QmltcVariable engine(u"QQmlEngine*"_s, u"engine"_s);
+ QmltcVariable parent(u"QObject*"_s, u"parent"_s, u"nullptr"_s);
+ QmltcVariable initializedCache(
+ u"[[maybe_unused]] const QSet<QString>&"_s,
+ u"initializedCache"_s,
+ u"{}"_s
+ );
+ QmltcVariable ctxtdata(u"const QQmlRefPointer<QQmlContextData>&"_s, u"parentContext"_s);
+ QmltcVariable finalizeFlag(u"bool"_s, u"canFinalize"_s);
+ current.baselineCtor.parameterList = { parent };
+ current.endInit.parameterList = { creator, engine };
+ current.setComplexBindings.parameterList = { creator, engine, initializedCache };
+ current.handleOnCompleted.parameterList = { creator };
+
+ if (documentRoot || inlineComponent) {
+ const QmltcVariable initializer(
+ u"[[maybe_unused]] qxp::function_ref<void(%1&)>"_s.arg(current.propertyInitializer.name),
+ u"initializer"_s,
+ u"[](%1&){}"_s.arg(current.propertyInitializer.name));
+
+ current.init.parameterList = { creator, engine, ctxtdata, finalizeFlag, initializer };
+ current.beginClass.parameterList = { creator, finalizeFlag };
+ current.completeComponent.parameterList = { creator, finalizeFlag };
+ current.finalizeComponent.parameterList = { creator, finalizeFlag };
+
+ compileRequiredPropertiesBundle(current, type, m_typeResolver);
+
+ if (current.requiredPropertiesBundle) {
+ QmltcVariable bundle{
+ u"const %1&"_s.arg(current.requiredPropertiesBundle->name),
+ u"requiredPropertiesBundle"_s,
+ };
+ current.externalCtor.parameterList = { engine, bundle, parent, initializer };
+ } else {
+ current.externalCtor.parameterList = { engine, parent, initializer };
+ }
+ } else {
+ current.externalCtor.parameterList = { creator, engine, parent };
+ current.init.parameterList = { creator, engine, ctxtdata };
+ current.beginClass.parameterList = { creator };
+ current.completeComponent.parameterList = { creator };
+ current.finalizeComponent.parameterList = { creator };
+ }
+
+ current.externalCtor.initializerList = { current.baselineCtor.name + u"(" + parent.name
+ + u")" };
+ if (QQmlJSUtils::hasCompositeBase(type)) {
+ // call parent's (QML type's) basic ctor from this. that one will take
+ // care about QObject::setParent()
+ current.baselineCtor.initializerList = { baseClass + u"(" + parent.name + u")" };
+ } else {
+ // default call to ctor is enough, but QQml_setParent_noEvent() is
+ // needed (note: faster? version of QObject::setParent())
+ current.baselineCtor.body << u"QQml_setParent_noEvent(this, " + parent.name + u");";
+ }
+
+ // compilation stub:
+ current.externalCtor.body << u"Q_UNUSED(engine)"_s;
+ if (documentRoot || inlineComponent) {
+ compileRootExternalConstructorBody(current, type);
+ } else {
+ current.externalCtor.body << u"// not document root:"_s;
+ // just call init, we don't do any setup here otherwise
+ current.externalCtor.body << current.init.name
+ + u"(creator, engine, QQmlData::get(parent)->outerContext);";
+ }
+
+ if (isSingleton) {
+ // see https://doc.qt.io/qt-6/qqmlengine.html#QML_SINGLETON for context
+ current.mocCode.append(u"QML_SINGLETON"_s);
+ auto &staticCreate = current.staticCreate.emplace();
+ staticCreate.comments
+ << u"Used by the engine for singleton creation."_s
+ << u"See also \\l {https://doc.qt.io/qt-6/qqmlengine.html#QML_SINGLETON}."_s;
+ staticCreate.type = QQmlJSMetaMethodType::StaticMethod;
+ staticCreate.access = QQmlJSMetaMethod::Public;
+ staticCreate.name = u"create"_s;
+ staticCreate.returnType = u"%1 *"_s.arg(current.cppType);
+ QmltcVariable jsEngine(u"QJSEngine*"_s, u"jsEngine"_s);
+ staticCreate.parameterList = { engine, jsEngine };
+ staticCreate.body << u"Q_UNUSED(jsEngine);"_s
+ << u"%1 *result = new %1(engine, nullptr);"_s.arg(current.cppType)
+ << u"return result;"_s;
+ }
+ auto postponedQmlContextSetup = generator.generate_initCode(current, type);
+ generator.generate_endInitCode(current, type);
+ generator.generate_setComplexBindingsCode(current, type);
+ generator.generate_beginClassCode(current, type);
+ generator.generate_completeComponentCode(current, type);
+ generator.generate_finalizeComponentCode(current, type);
+ generator.generate_handleOnCompletedCode(current, type);
+
+ compileElements(current, type);
+}
+
+template<typename Iterator>
+static Iterator partitionBindings(Iterator first, Iterator last)
+{
+ // NB: the code generator cares about script bindings being processed at a
+ // later point, so we should sort or partition the range. we do a stable
+ // partition since the relative order of binding evaluation affects the UI
+ return std::stable_partition(first, last, [](const QQmlJSMetaPropertyBinding &b) {
+ // we want complex bindings to be at the end, so do the negation
+ return !QmltcCompiler::isComplexBinding(b);
+ });
+}
+
+// Populates the propertyInitializer of the current type based on the
+// available properties.
+//
+// A propertyInitializer is a generated class that provides a
+// restricted interface that only allows setting property values and
+// internally keep tracks of which properties where actually set,
+// intended to be used to allow the user to set up the initial values
+// when creating an instance of a component.
+//
+// For each property of the current type that is known, is not private
+// and is writable, a setter method is generated.
+// Each setter method knows how to set a specific property, so as to
+// provide a strongly typed interface to property setting, as if the
+// relevant C++ type was used directly.
+//
+// Each setter uses the write method for the proprerty when available
+// and otherwise falls back to a the more generic
+// `QObject::setProperty` for properties where a WRITE method is not
+// available or in scope.
+static void compilePropertyInitializer(QmltcType &current, const QQmlJSScope::ConstPtr &type) {
+ static auto isFromExtension = [](const QQmlJSMetaProperty &property, const QQmlJSScope::ConstPtr &scope) {
+ return scope->ownerOfProperty(scope, property.propertyName()).extensionSpecifier != QQmlJSScope::NotExtension;
+ };
+
+ current.propertyInitializer.constructor.initializerList << u"component{component}"_s;
+
+ auto properties = type->properties().values();
+ for (auto& property: properties) {
+ if (property.index() == -1) continue;
+ if (property.isPrivate()) continue;
+ if (!property.isWritable() && !qIsReferenceTypeList(property)) continue;
+
+ const QString name = property.propertyName();
+
+ current.propertyInitializer.propertySetters.emplace_back();
+ auto& compiledSetter = current.propertyInitializer.propertySetters.back();
+
+ compiledSetter.userVisible = true;
+ compiledSetter.returnType = u"void"_s;
+ compiledSetter.name = QmltcPropertyData(property).write;
+
+ if (qIsReferenceTypeList(property)) {
+ compiledSetter.parameterList.emplaceBack(
+ QQmlJSUtils::constRefify(u"QList<%1*>"_s.arg(property.type()->valueType()->internalName())),
+ name + u"_", QString()
+ );
+ } else {
+ compiledSetter.parameterList.emplaceBack(
+ QQmlJSUtils::constRefify(getUnderlyingType(property)), name + u"_", QString()
+ );
+ }
+
+ if (qIsReferenceTypeList(property)) {
+ compiledSetter.body << u"QQmlListReference list_ref_(&%1, \"%2\");"_s.arg(
+ current.propertyInitializer.component.name, name
+ );
+ compiledSetter.body << u"list_ref_.clear();"_s;
+ compiledSetter.body << u"for (const auto& list_item_ : %1_)"_s.arg(name);
+ compiledSetter.body << u" list_ref_.append(list_item_);"_s;
+ } else if (
+ QQmlJSUtils::bindablePropertyHasDefaultAccessor(property, QQmlJSUtils::PropertyAccessor_Write)
+ ) {
+ compiledSetter.body << u"%1.%2().setValue(%3_);"_s.arg(
+ current.propertyInitializer.component.name, property.bindable(), name);
+ } else if (type->hasOwnProperty(name)) {
+ compiledSetter.body << u"%1.%2(%3_);"_s.arg(
+ current.propertyInitializer.component.name, QmltcPropertyData(property).write, name);
+ } else if (property.write().isEmpty() || isFromExtension(property, type)) {
+ // We can end here if a WRITE method is not available or
+ // if the method is available but not in this scope, so
+ // that we fallback to the string-based setters..
+ //
+ // For example, types that makes use of QML_EXTENDED
+ // types, will have the extension types properties
+ // available and with a WRITE method, but the WRITE method
+ // will not be available to the extended type, from C++,
+ // as the type does not directly inherit from the
+ // extension type.
+ //
+ // We specifically scope `setProperty` to `QObject` as
+ // certain types might have shadowed the method.
+ // For example, in QtQuick, some types have a property
+ // called `property` with a `setProperty` WRITE method
+ // that will produce the shadowing.
+ compiledSetter.body << u"%1.QObject::setProperty(\"%2\", QVariant::fromValue(%2_));"_s.arg(
+ current.propertyInitializer.component.name, name);
+ } else {
+ compiledSetter.body << u"%1.%2(%3_);"_s.arg(
+ current.propertyInitializer.component.name, property.write(), name);
+ }
+
+ compiledSetter.body << u"%1.insert(\"%2\");"_s.arg(
+ current.propertyInitializer.initializedCache.name, name);
+ }
+}
+
+void QmltcCompiler::compileTypeElements(QmltcType &current, const QQmlJSScope::ConstPtr &type)
+{
+ // compile components of a type:
+ // - enums
+ // - properties
+ // - methods
+ // - bindings
+
+ const auto enums = type->ownEnumerations();
+ current.enums.reserve(enums.size());
+ for (auto it = enums.begin(); it != enums.end(); ++it)
+ compileEnum(current, it.value());
+
+ auto properties = type->ownProperties().values();
+ current.properties.reserve(properties.size());
+ // Note: index() is the (future) meta property index, so make sure given
+ // properties are ordered by that index before compiling
+ std::sort(properties.begin(), properties.end(),
+ [](const QQmlJSMetaProperty &x, const QQmlJSMetaProperty &y) {
+ return x.index() < y.index();
+ });
+ for (const QQmlJSMetaProperty &p : std::as_const(properties)) {
+ if (p.index() == -1) {
+ recordError(type->sourceLocation(),
+ u"Internal error: property '%1' has incomplete information"_s.arg(
+ p.propertyName()));
+ continue;
+ }
+ if (p.isAlias()) {
+ compileAlias(current, p, type);
+ } else {
+ compileProperty(current, p, type);
+ }
+ }
+
+ const auto methods = type->ownMethods();
+ for (const QQmlJSMetaMethod &m : methods)
+ compileMethod(current, m, type);
+
+ auto bindings = type->ownPropertyBindingsInQmlIROrder();
+ partitionBindings(bindings.begin(), bindings.end());
+
+ compilePropertyInitializer(current, type);
+ compileBinding(current, bindings.begin(), bindings.end(), type, { type });
+}
+
+void QmltcCompiler::compileEnum(QmltcType &current, const QQmlJSMetaEnum &e)
+{
+ const auto intValues = e.values();
+ QStringList values;
+ values.reserve(intValues.size());
+ std::transform(intValues.cbegin(), intValues.cend(), std::back_inserter(values),
+ [](int x) { return QString::number(x); });
+
+ // structure: (C++ type name, enum keys, enum values, MOC line)
+ current.enums.emplaceBack(e.name(), e.keys(), std::move(values),
+ u"Q_ENUM(%1)"_s.arg(e.name()));
+}
+
+static QList<QmltcVariable>
+compileMethodParameters(const QList<QQmlJSMetaParameter> &parameterInfos, bool allowUnnamed = false)
+{
+ QList<QmltcVariable> parameters;
+ const auto size = parameterInfos.size();
+ parameters.reserve(size);
+ for (qsizetype i = 0; i < size; ++i) {
+ const auto &p = parameterInfos[i];
+ Q_ASSERT(p.type()); // assume verified
+ QString name = p.name();
+ Q_ASSERT(allowUnnamed || !name.isEmpty()); // assume verified
+ if (name.isEmpty() && allowUnnamed)
+ name = u"unnamed_" + QString::number(i);
+
+ QString internalName;
+ const QQmlJSScope::AccessSemantics semantics = p.type()->accessSemantics();
+
+ switch (semantics) {
+ case QQmlJSScope::AccessSemantics::Reference:
+ if (p.typeQualifier() == QQmlJSMetaParameter::Const)
+ internalName = u"const "_s;
+ internalName += u"%1*"_s.arg(p.type()->internalName());
+ break;
+ case QQmlJSScope::AccessSemantics::Value:
+ case QQmlJSScope::AccessSemantics::Sequence:
+ internalName = u"passByConstRefOrValue<%1>"_s.arg(p.type()->internalName());
+ break;
+ case QQmlJSScope::AccessSemantics::None:
+ Q_ASSERT(false); // or maybe print an error message
+ }
+ parameters.emplaceBack(internalName, name, QString());
+ }
+ return parameters;
+}
+
+static QString figureReturnType(const QQmlJSMetaMethod &m)
+{
+ const bool isVoidMethod =
+ m.returnTypeName() == u"void" || m.methodType() == QQmlJSMetaMethodType::Signal;
+ Q_ASSERT(isVoidMethod || m.returnType());
+ QString type;
+ if (isVoidMethod) {
+ type = u"void"_s;
+ } else {
+ type = m.returnType()->augmentedInternalName();
+ }
+ return type;
+}
+
+void QmltcCompiler::compileMethod(QmltcType &current, const QQmlJSMetaMethod &m,
+ const QQmlJSScope::ConstPtr &owner)
+{
+ const auto returnType = figureReturnType(m);
+
+ const QList<QmltcVariable> compiledParams = compileMethodParameters(m.parameters());
+ const auto methodType = m.methodType();
+
+ QStringList code;
+ if (methodType != QQmlJSMetaMethodType::Signal) {
+ QmltcCodeGenerator urlGenerator { m_url, m_visitor };
+ QmltcCodeGenerator::generate_callExecuteRuntimeFunction(
+ &code, urlGenerator.urlMethodName() + u"()",
+ owner->ownRuntimeFunctionIndex(m.jsFunctionIndex()), u"this"_s, returnType,
+ compiledParams);
+ }
+
+ QmltcMethod compiled {};
+ compiled.returnType = returnType;
+ compiled.name = m.methodName();
+ compiled.parameterList = std::move(compiledParams);
+ compiled.body = std::move(code);
+ compiled.type = methodType;
+ compiled.access = m.access();
+ if (methodType != QQmlJSMetaMethodType::Signal) {
+ compiled.declarationPrefixes << u"Q_INVOKABLE"_s;
+ compiled.userVisible = m.access() == QQmlJSMetaMethod::Public;
+ } else {
+ compiled.userVisible = !m.isImplicitQmlPropertyChangeSignal();
+ }
+ current.functions.emplaceBack(compiled);
+}
+
+/*! \internal
+ Compiles an extra set of methods for Lists, that makes manipulating lists easier from C++
+ for the user.
+*/
+void QmltcCompiler::compileExtraListMethods(QmltcType &current, const QQmlJSMetaProperty &p)
+{
+ QmltcPropertyData data(p);
+ const QString valueType = p.type()->valueType()->internalName() + u'*';
+ const QString variableName = data.read + u"()"_s;
+ const QStringList ownershipWarning = {
+ u"\\note {This method does not change the ownership of its argument."_s,
+ u"The caller is responsible for setting the argument's \\c {QObject::parent} or"_s,
+ u"for ensuring that the argument lives long enough."_s,
+ u"For example, an argument created with \\c {createObject()} that has no parent"_s,
+ u"will eventually be garbage-collected, leaving a dangling pointer.}"_s
+ };
+
+ // generate append() sugar for users
+ {
+ QmltcMethod append{};
+ append.comments.emplaceBack(u"\\brief Append an element to %1."_s.arg(data.read));
+ append.comments << ownershipWarning;
+ append.returnType = u"void"_s;
+ append.name = u"%1Append"_s.arg(data.read);
+ append.parameterList.emplaceBack(valueType, u"toBeAppended"_s);
+
+ append.body << u"auto q_qmltc_localList = %1;"_s.arg(variableName);
+ append.body
+ << u"q_qmltc_localList.append(std::addressof(q_qmltc_localList), toBeAppended);"_s;
+ // append.body << u"Q_EMIT %1();"_s.arg(data.notify); // uncomment this when QTBUG-106587 is
+ // resolved
+ append.userVisible = true;
+ current.functions.emplaceBack(append);
+ }
+
+ // generate count() sugar for users
+ {
+ QmltcMethod count{};
+ count.comments.emplaceBack(u"\\brief Number of elements in %1."_s.arg(data.read));
+ count.returnType = u"int"_s;
+ count.name = u"%1Count"_s.arg(data.read);
+
+ count.body << u"auto q_qmltc_localList = %1;"_s.arg(variableName);
+ count.body << u"int result = q_qmltc_localList.count(std::addressof(q_qmltc_localList));"_s;
+ count.body << u"return result;"_s;
+ count.userVisible = true;
+ current.functions.emplaceBack(count);
+ }
+
+ // generate at() sugar for users
+ {
+ QmltcMethod at{};
+ at.comments.emplaceBack(u"\\brief Access an element in %1."_s.arg(data.read));
+ at.returnType = valueType;
+ at.name = u"%1At"_s.arg(data.read);
+ at.parameterList.emplaceBack(u"qsizetype"_s, u"position"_s, QString());
+
+ at.body << u"auto q_qmltc_localList = %1;"_s.arg(variableName);
+ at.body << u"auto result = q_qmltc_localList.at(std::addressof(q_qmltc_localList), position);"_s;
+ at.body << u"return result;"_s;
+ at.userVisible = true;
+ current.functions.emplaceBack(at);
+ }
+
+ // generate clear() sugar for users
+ {
+ QmltcMethod clear{};
+ clear.comments.emplaceBack(u"\\brief Clear %1."_s.arg(data.read));
+ clear.returnType = u"void"_s;
+ clear.name = u"%1Clear"_s.arg(data.read);
+
+ clear.body << u"auto q_qmltc_localList = %1;"_s.arg(variableName);
+ clear.body << u"q_qmltc_localList.clear(std::addressof(q_qmltc_localList));"_s;
+ // clear.body << u"Q_EMIT %1();"_s.arg(data.notify); // uncomment this when QTBUG-106587 is
+ // resolved
+ clear.userVisible = true;
+ current.functions.emplaceBack(clear);
+ }
+
+ // generate replace() sugar for users
+ {
+ QmltcMethod replace{};
+ replace.comments.emplaceBack(u"\\brief Replace an element in %1."_s.arg(data.read));
+ replace.comments << ownershipWarning;
+ replace.returnType = u"void"_s;
+ replace.name = u"%1Replace"_s.arg(data.read);
+ replace.parameterList.emplaceBack(u"qsizetype"_s, u"position"_s, QString());
+ replace.parameterList.emplaceBack(valueType, u"element"_s,
+ QString());
+
+ replace.body << u"auto q_qmltc_localList = %1;"_s.arg(variableName);
+ replace.body
+ << u"q_qmltc_localList.replace(std::addressof(q_qmltc_localList), position, element);"_s;
+ // replace.body << u"Q_EMIT %1();"_s.arg(data.notify); // uncomment this when QTBUG-106587
+ // is resolved
+ replace.userVisible = true;
+ current.functions.emplaceBack(replace);
+ }
+
+ // generate removeLast() sugar for users
+ {
+ QmltcMethod removeLast{};
+ removeLast.comments.emplaceBack(u"\\brief Remove the last element in %1."_s.arg(data.read));
+ removeLast.returnType = u"void"_s;
+ removeLast.name = u"%1RemoveLast"_s.arg(data.read);
+
+ removeLast.body << u"auto q_qmltc_localList = %1;"_s.arg(variableName);
+ removeLast.body << u"q_qmltc_localList.removeLast(std::addressof(q_qmltc_localList));"_s;
+ // removeLast.body << u"Q_EMIT %1();"_s.arg(data.notify); // uncomment this when
+ // QTBUG-106587 is resolved
+
+ removeLast.userVisible = true;
+ current.functions.emplaceBack(removeLast);
+ }
+}
+
+void QmltcCompiler::compileProperty(QmltcType &current, const QQmlJSMetaProperty &p,
+ const QQmlJSScope::ConstPtr &owner)
+{
+ Q_ASSERT(!p.isAlias()); // will be handled separately
+ Q_ASSERT(p.type());
+
+ const QString name = p.propertyName();
+ const QString variableName = u"m_" + name;
+ const QString underlyingType = getUnderlyingType(p);
+ if (qIsReferenceTypeList(p)) {
+ const QString storageName = variableName + u"_storage";
+ current.variables.emplaceBack(
+ u"QList<" + p.type()->valueType()->internalName() + u"*>", storageName,
+ QString());
+ current.baselineCtor.initializerList.emplaceBack(variableName + u"(" + underlyingType
+ + u"(this, std::addressof(" + storageName
+ + u")))");
+ compileExtraListMethods(current, p);
+ }
+
+ // along with property, also add relevant moc code, so that we can use the
+ // property in Qt/QML contexts
+ QStringList mocPieces;
+ mocPieces.reserve(10);
+ mocPieces << underlyingType << name;
+
+ QmltcPropertyData compilationData(p);
+
+ // 1. add setter and getter
+ // If p.isList(), it's a QQmlListProperty. Then you can write the underlying list through
+ // the QQmlListProperty object retrieved with the getter. Setting it would make no sense.
+ if (p.isWritable() && !qIsReferenceTypeList(p)) {
+ QmltcMethod setter {};
+ setter.returnType = u"void"_s;
+ setter.name = compilationData.write;
+ // QmltcVariable
+ setter.parameterList.emplaceBack(QQmlJSUtils::constRefify(underlyingType), name + u"_",
+ u""_s);
+ setter.body << variableName + u".setValue(" + name + u"_);";
+ setter.body << u"Q_EMIT " + compilationData.notify + u"();";
+ setter.userVisible = true;
+ current.functions.emplaceBack(setter);
+ mocPieces << u"WRITE"_s << setter.name;
+ }
+
+ QmltcMethod getter {};
+ getter.returnType = underlyingType;
+ getter.name = compilationData.read;
+ getter.body << u"return " + variableName + u".value();";
+ getter.userVisible = true;
+ current.functions.emplaceBack(getter);
+ mocPieces << u"READ"_s << getter.name;
+
+ // 2. add bindable
+ if (!qIsReferenceTypeList(p)) {
+ QmltcMethod bindable {};
+ bindable.returnType = u"QBindable<" + underlyingType + u">";
+ bindable.name = compilationData.bindable;
+ bindable.body << u"return QBindable<" + underlyingType + u">(std::addressof(" + variableName
+ + u"));";
+ bindable.userVisible = true;
+ current.functions.emplaceBack(bindable);
+ mocPieces << u"BINDABLE"_s << bindable.name;
+ }
+
+ // 3. add/check notify (actually, this is already done inside QmltcVisitor)
+
+ if (owner->isPropertyRequired(name))
+ mocPieces << u"REQUIRED"_s;
+
+ // 4. add moc entry
+ // e.g. Q_PROPERTY(QString p READ getP WRITE setP BINDABLE bindableP)
+ current.mocCode << u"Q_PROPERTY(" + mocPieces.join(u" "_s) + u")";
+
+ // 5. add extra moc entry if this property is marked default
+ if (name == owner->defaultPropertyName())
+ current.mocCode << u"Q_CLASSINFO(\"DefaultProperty\", \"%1\")"_s.arg(name);
+
+ // structure: (C++ type name, name, C++ class name, C++ signal name)
+ current.properties.emplaceBack(underlyingType, variableName, current.cppType,
+ compilationData.notify);
+}
+
+/*!
+ * \internal
+ *
+ * Models one step of the alias resolution. If the current alias to be resolved
+ * points to \c {x.y.z} and that \c {x.y} is already resolved, then this struct
+ * contains the information on how to obtain the \c {z} part from \c {x.y}.
+ */
+struct AliasResolutionFrame
+{
+ /*!
+ * \internal
+ *
+ * Placeholder for the current resolved state. It is replaced later with
+ * the result from previous resolutions from the \c QStack<AliasResolutionFrame>.
+ *
+ * \sa unpackFrames()
+ */
+ static QString inVar;
+
+ /*!
+ * \internal
+ *
+ * Steps to access this value as a list of C++ statements, to be used in
+ * conjunction with \c {epilogue}.
+ */
+ QStringList prologue;
+
+ /*!
+ * \internal
+ *
+ * Steps to finish the statements of the \c prologue (e.g. closing brackets).
+ */
+ QStringList epilogue;
+
+ /*!
+ * \internal
+ *
+ * Instructions on how to write the property, after it was loaded with the
+ * instructions from \c prologue. Has to happen before \c epilogue.
+ */
+ QStringList epilogueForWrite;
+
+ /*!
+ * \internal
+ *
+ * Name of the variable holding the result of this resolution step, to be
+ * used in the following resolution steps.
+ */
+ QString outVar;
+};
+// special string replaced by outVar of the previous frame
+QString AliasResolutionFrame::inVar = QStringLiteral("__QMLTC_ALIAS_FRAME_INPUT_VAR__");
+
+/*!
+ * \internal
+ *
+ * Process the frames by replacing the placeholder \c invar
+ * used in \c epilogueForWrite and \c prologue with the result
+ * obtained from the previous frame.
+ */
+static void unpackFrames(QStack<AliasResolutionFrame> &frames)
+{
+ if (frames.size() < 2)
+ return;
+
+ // assume first frame is fine
+ auto prev = frames.begin();
+ for (auto it = std::next(prev); it != frames.end(); ++it, ++prev) {
+ for (QString &line : it->prologue)
+ line.replace(AliasResolutionFrame::inVar, prev->outVar);
+ for (QString &line : it->epilogueForWrite)
+ line.replace(AliasResolutionFrame::inVar, prev->outVar);
+ it->outVar.replace(AliasResolutionFrame::inVar, prev->outVar);
+ }
+}
+
+template<typename Projection>
+static QStringList joinFrames(const QStack<AliasResolutionFrame> &frames, Projection project)
+{
+ QStringList joined;
+ for (const AliasResolutionFrame &frame : frames)
+ joined += project(frame);
+ return joined;
+}
+
+void QmltcCompiler::compileAlias(QmltcType &current, const QQmlJSMetaProperty &alias,
+ const QQmlJSScope::ConstPtr &owner)
+{
+ const QString aliasName = alias.propertyName();
+ Q_ASSERT(!aliasName.isEmpty());
+
+ QStringList aliasExprBits = alias.aliasExpression().split(u'.');
+ Q_ASSERT(!aliasExprBits.isEmpty());
+
+ QStack<AliasResolutionFrame> frames;
+
+ QQmlJSUtils::AliasResolutionVisitor aliasVisitor;
+ qsizetype i = 0;
+ aliasVisitor.reset = [&]() {
+ frames.clear();
+ i = 0; // we use it in property processing
+
+ // first frame is a dummy one:
+ frames.push(
+ AliasResolutionFrame { QStringList(), QStringList(), QStringList(), u"this"_s });
+ };
+ aliasVisitor.processResolvedId = [&](const QQmlJSScope::ConstPtr &type) {
+ Q_ASSERT(type);
+ if (owner != type) { // cannot start at `this`, need to fetch object through context
+ const int id = m_visitor->runtimeId(type);
+ Q_ASSERT(id >= 0); // since the type is found by id, it must have an id
+
+ AliasResolutionFrame queryIdFrame {};
+ Q_ASSERT(frames.top().outVar == u"this"_s); // so inVar would be "this" as well
+ queryIdFrame.prologue << u"auto context = %1::q_qmltc_thisContext;"_s.arg(
+ owner->internalName());
+
+ // doing the above allows us to lookup id object by index (fast)
+ queryIdFrame.outVar = u"alias_objectById_" + aliasExprBits.front(); // unique enough
+ const QString cppType = (m_visitor->qmlComponentIndex(type) == -1)
+ ? type->internalName()
+ : u"QQmlComponent"_s;
+ queryIdFrame.prologue << u"auto " + queryIdFrame.outVar + u" = static_cast<" + cppType
+ + u"*>(context->idValue(" + QString::number(id) + u"));";
+ queryIdFrame.prologue << u"Q_ASSERT(" + queryIdFrame.outVar + u");";
+
+ frames.push(queryIdFrame);
+ }
+ };
+ aliasVisitor.processResolvedProperty = [&](const QQmlJSMetaProperty &p,
+ const QQmlJSScope::ConstPtr &owner) {
+ AliasResolutionFrame queryPropertyFrame {};
+
+ auto [extensionPrologue, extensionAccessor, extensionEpilogue] =
+ QmltcCodeGenerator::wrap_extensionType(
+ owner, p,
+ QmltcCodeGenerator::wrap_privateClass(AliasResolutionFrame::inVar, p));
+ QString inVar = extensionAccessor;
+ queryPropertyFrame.prologue += extensionPrologue;
+ if (p.type()->accessSemantics() == QQmlJSScope::AccessSemantics::Value) {
+ // we need to read the property to a local variable and then
+ // write the updated value once the actual operation is done
+ const QString aliasVar = u"alias_" + QString::number(i); // should be fairly unique
+ ++i;
+ queryPropertyFrame.prologue
+ << u"auto " + aliasVar + u" = " + inVar + u"->" + p.read() + u"();";
+ queryPropertyFrame.epilogueForWrite
+ << inVar + u"->" + p.write() + u"(" + aliasVar + u");";
+ // NB: since accessor becomes a value type, wrap it into an
+ // addressof operator so that we could access it as a pointer
+ inVar = QmltcCodeGenerator::wrap_addressof(aliasVar); // reset
+ } else {
+ inVar += u"->" + p.read() + u"()"; // update
+ }
+ queryPropertyFrame.outVar = inVar;
+ queryPropertyFrame.epilogue += extensionEpilogue;
+
+ frames.push(queryPropertyFrame);
+ };
+
+ QQmlJSUtils::ResolvedAlias result =
+ QQmlJSUtils::resolveAlias(m_typeResolver, alias, owner, aliasVisitor);
+ Q_ASSERT(result.kind != QQmlJSUtils::AliasTarget_Invalid);
+
+ unpackFrames(frames);
+
+ if (result.kind == QQmlJSUtils::AliasTarget_Property) {
+ // we don't need the last frame here
+ frames.pop();
+
+ // instead, add a custom frame
+ AliasResolutionFrame customFinalFrame {};
+ auto [extensionPrologue, extensionAccessor, extensionEpilogue] =
+ QmltcCodeGenerator::wrap_extensionType(
+ result.owner, result.property,
+ QmltcCodeGenerator::wrap_privateClass(frames.top().outVar,
+ result.property));
+ customFinalFrame.prologue = extensionPrologue;
+ customFinalFrame.outVar = extensionAccessor;
+ customFinalFrame.epilogue = extensionEpilogue;
+ frames.push(customFinalFrame);
+ }
+
+ const QString latestAccessor = frames.top().outVar;
+ const QStringList prologue =
+ joinFrames(frames, [](const AliasResolutionFrame &frame) { return frame.prologue; });
+ const QStringList epilogue =
+ joinFrames(frames, [](const AliasResolutionFrame &frame) { return frame.epilogue; });
+ const QString underlyingType = (result.kind == QQmlJSUtils::AliasTarget_Property)
+ ? getUnderlyingType(result.property)
+ : result.owner->internalName() + u" *";
+
+ QStringList mocLines;
+ mocLines.reserve(10);
+ mocLines << underlyingType << aliasName;
+
+ QmltcPropertyData compilationData(aliasName);
+ // 1. add setter and getter
+ QmltcMethod getter {};
+ getter.returnType = underlyingType;
+ getter.name = compilationData.read;
+ getter.body += prologue;
+ if (result.kind == QQmlJSUtils::AliasTarget_Property) {
+ if (QString read = result.property.read(); !read.isEmpty()
+ && !QQmlJSUtils::bindablePropertyHasDefaultAccessor(
+ result.property, QQmlJSUtils::PropertyAccessor_Read)) {
+ getter.body << u"return %1->%2();"_s.arg(latestAccessor, read);
+ } else { // use QObject::property() as a fallback when read method is unknown
+ getter.body << u"return qvariant_cast<%1>(%2->property(\"%3\"));"_s.arg(
+ underlyingType, latestAccessor, result.property.propertyName());
+ }
+ } else { // AliasTarget_Object
+ getter.body << u"return " + latestAccessor + u";";
+ }
+ getter.body += epilogue;
+ getter.userVisible = true;
+ current.functions.emplaceBack(getter);
+ mocLines << u"READ"_s << getter.name;
+
+ if (result.property.isWritable()) {
+ Q_ASSERT(result.kind == QQmlJSUtils::AliasTarget_Property); // property is invalid otherwise
+ QmltcMethod setter {};
+ setter.returnType = u"void"_s;
+ setter.name = compilationData.write;
+
+ const QString setName = result.property.write();
+ QList<QQmlJSMetaMethod> methods = result.owner->methods(setName);
+ if (methods.isEmpty()) { // when we are compiling the property as well
+ // QmltcVariable
+ setter.parameterList.emplaceBack(QQmlJSUtils::constRefify(underlyingType),
+ aliasName + u"_", u""_s);
+ } else {
+ setter.parameterList = compileMethodParameters(methods.at(0).parameters(),
+ /* allow unnamed = */ true);
+ }
+
+ setter.body += prologue;
+ QStringList parameterNames;
+ parameterNames.reserve(setter.parameterList.size());
+ std::transform(setter.parameterList.cbegin(), setter.parameterList.cend(),
+ std::back_inserter(parameterNames),
+ [](const QmltcVariable &x) { return x.name; });
+ QString commaSeparatedParameterNames = parameterNames.join(u", "_s);
+ if (!setName.isEmpty()
+ && !QQmlJSUtils::bindablePropertyHasDefaultAccessor(
+ result.property, QQmlJSUtils::PropertyAccessor_Write)) {
+ setter.body << u"%1->%2(%3);"_s.arg(latestAccessor, setName,
+ commaSeparatedParameterNames);
+ } else { // use QObject::setProperty() as fallback when write method is unknown
+ Q_ASSERT(parameterNames.size() == 1);
+ const QString variantName = u"var_" + aliasName; // fairly unique
+ setter.body << u"QVariant %1;"_s.arg(variantName);
+ setter.body << u"%1.setValue(%2);"_s.arg(variantName, commaSeparatedParameterNames);
+ setter.body << u"%1->setProperty(\"%2\", std::move(%3));"_s.arg(
+ latestAccessor, result.property.propertyName(), variantName);
+ }
+ setter.body += joinFrames(
+ frames, [](const AliasResolutionFrame &frame) { return frame.epilogueForWrite; });
+ setter.body += epilogue; // NB: *after* epilogueForWrite - see prologue construction
+ setter.userVisible = true;
+ current.functions.emplaceBack(setter);
+ mocLines << u"WRITE"_s << setter.name;
+ }
+ // 2. add bindable
+ if (QString bindableName = result.property.bindable(); !bindableName.isEmpty()) {
+ Q_ASSERT(result.kind == QQmlJSUtils::AliasTarget_Property); // property is invalid otherwise
+ QmltcMethod bindable {};
+ bindable.returnType = u"QBindable<" + underlyingType + u">";
+ bindable.name = compilationData.bindable;
+ bindable.body += prologue;
+ bindable.body << u"return " + latestAccessor + u"->" + bindableName + u"()" + u";";
+ bindable.body += epilogue;
+ bindable.userVisible = true;
+ current.functions.emplaceBack(bindable);
+ mocLines << u"BINDABLE"_s << bindable.name;
+ }
+
+ // 3. add notify - which is pretty special
+ // step 1: generate the moc instructions
+ // mimic the engines behavior: do it even if the notify will never be emitted
+ if (const QString aliasNotifyName = alias.notify(); !aliasNotifyName.isEmpty()) {
+
+ Q_ASSERT(result.kind == QQmlJSUtils::AliasTarget_Property); // property is invalid otherwise
+
+ mocLines << u"NOTIFY"_s << aliasNotifyName;
+ }
+
+ // step 2: connect the notifier to the aliased property notifier, if this latter exists
+ // otherwise, mimic the engines behavior and generate a useless notify
+ if (const QString notifyName = result.property.notify(); !notifyName.isEmpty()) {
+ auto notifyFrames = frames;
+ notifyFrames.pop(); // we don't need the last frame at all in this case
+
+ const QStringList notifyPrologue = joinFrames(
+ frames, [](const AliasResolutionFrame &frame) { return frame.prologue; });
+ const QStringList notifyEpilogue = joinFrames(
+ frames, [](const AliasResolutionFrame &frame) { return frame.epilogue; });
+
+ // notify is very special
+ current.endInit.body << u"{ // alias notify connection:"_s;
+ current.endInit.body += notifyPrologue;
+ // TODO: use non-private accessor since signals must exist on the public
+ // type, not on the private one -- otherwise, you can't connect to a
+ // private property signal in C++ and so it is useless (hence, use
+ // public type)
+ const QString cppType = (m_visitor->qmlComponentIndex(result.owner) == -1)
+ ? result.owner->internalName()
+ : u"QQmlComponent"_s;
+ const QString latestAccessorNonPrivate = notifyFrames.top().outVar;
+ current.endInit.body << u"QObject::connect(" + latestAccessorNonPrivate + u", &" + cppType
+ + u"::" + notifyName + u", this, &" + current.cppType + u"::"
+ + compilationData.notify + u");";
+ current.endInit.body += notifyEpilogue;
+ current.endInit.body << u"}"_s;
+ }
+
+ if (QString resetName = result.property.reset(); !resetName.isEmpty()) {
+ Q_ASSERT(result.kind == QQmlJSUtils::AliasTarget_Property); // property is invalid otherwise
+ QmltcMethod reset {};
+ reset.returnType = u"void"_s;
+ reset.name = compilationData.reset;
+ reset.body += prologue;
+ reset.body << latestAccessor + u"->" + resetName + u"()" + u";";
+ reset.body += epilogue;
+ reset.userVisible = true;
+ current.functions.emplaceBack(reset);
+ mocLines << u"RESET"_s << reset.name;
+ }
+
+ // mimic the engines behavior: aliases are never constants
+ // mocLines << u"CONSTANT"_s;
+ // mimic the engines behavior: aliases are never stored
+ mocLines << u"STORED"_s << u"false"_s;
+ // mimic the engines behavior: aliases are never designable
+ mocLines << u"DESIGNABLE"_s << u"false"_s;
+
+ // 4. add moc entry
+ // Q_PROPERTY(QString text READ text WRITE setText BINDABLE bindableText NOTIFY textChanged)
+ current.mocCode << u"Q_PROPERTY(" + mocLines.join(u" "_s) + u")";
+
+ // 5. add extra moc entry if this alias is default one
+ if (aliasName == owner->defaultPropertyName()) {
+ // Q_CLASSINFO("DefaultProperty", propertyName)
+ current.mocCode << u"Q_CLASSINFO(\"DefaultProperty\", \"%1\")"_s.arg(aliasName);
+ }
+}
+
+static QString generate_callCompilationUnit(const QString &urlMethodName)
+{
+ return u"QQmlEnginePrivate::get(engine)->compilationUnitFromUrl(%1())"_s.arg(urlMethodName);
+}
+
+static std::pair<QQmlJSMetaProperty, int> getMetaPropertyIndex(const QQmlJSScope::ConstPtr &scope,
+ const QString &propertyName);
+
+/*!
+ * \internal
+ * Helper method used to keep compileBindingByType() readable.
+ */
+void QmltcCompiler::compileObjectBinding(QmltcType &current,
+ const QQmlJSMetaPropertyBinding &binding,
+ const QQmlJSScope::ConstPtr &type,
+ const BindingAccessorData &accessor)
+{
+ Q_ASSERT(binding.bindingType() == QQmlSA::BindingType::Object);
+
+ const QString &propertyName = binding.propertyName();
+ const QQmlJSMetaProperty property = type->property(propertyName);
+ QQmlJSScope::ConstPtr propertyType = property.type();
+
+ // NB: object is compiled with compileType(), here just need to use it
+ auto object = binding.objectType();
+
+ // Note: despite a binding being set for `accessor`, we use "this" as a
+ // parent of a created object. Both attached and grouped properties are
+ // parented by "this", so lifetime-wise we should be fine
+ const QString qobjectParent = u"this"_s;
+
+ if (!propertyType) {
+ recordError(binding.sourceLocation(),
+ u"Binding on property '" + propertyName + u"' of unknown type");
+ return;
+ }
+
+ const auto addObjectBinding = [&](const QString &value) {
+ if (qIsReferenceTypeList(property)) {
+ Q_ASSERT(unprocessedListProperty == property || unprocessedListBindings.empty());
+ unprocessedListBindings.append(value);
+ unprocessedListProperty = property;
+ } else {
+ QmltcCodeGenerator::generate_assignToProperty(&current.endInit.body, type, property,
+ value, accessor.name, true);
+ }
+ };
+
+ // special case of implicit or explicit component:
+ if (qsizetype index = m_visitor->qmlComponentIndex(object); index >= 0) {
+ const QString objectName = newSymbol(u"sc"_s);
+
+ const qsizetype creationIndex = m_visitor->creationIndex(object);
+
+ QStringList *block = (creationIndex == -1) ? &current.endInit.body : &current.init.body;
+ *block << u"{"_s;
+ *block << QStringLiteral("auto thisContext = QQmlData::get(%1)->outerContext;")
+ .arg(qobjectParent);
+ *block << QStringLiteral("auto %1 = QQmlObjectCreator::createComponent(engine, "
+ "%2, %3, %4, thisContext);")
+ .arg(objectName, generate_callCompilationUnit(m_urlMethodName),
+ QString::number(index), qobjectParent);
+ *block << QStringLiteral("thisContext->installContext(QQmlData::get(%1), "
+ "QQmlContextData::OrdinaryObject);")
+ .arg(objectName);
+
+ // objects wrapped in implicit components do not have visible ids,
+ // however, explicit components can have an id and that one is going
+ // to be visible in the common document context
+ if (creationIndex != -1) {
+ // explicit component
+ Q_ASSERT(object->isComposite());
+ Q_ASSERT(object->baseType()->internalName() == u"QQmlComponent"_s);
+
+ if (int id = m_visitor->runtimeId(object); id >= 0) {
+ QString idString = m_visitor->addressableScopes().id(object, object);
+ if (idString.isEmpty())
+ idString = u"<unknown>"_s;
+ QmltcCodeGenerator::generate_setIdValue(block, u"thisContext"_s, id, objectName,
+ idString);
+ }
+
+ const QString creationIndexStr = QString::number(creationIndex);
+ *block << QStringLiteral("creator->set(%1, %2);").arg(creationIndexStr, objectName);
+ Q_ASSERT(block == &current.init.body);
+ current.endInit.body << QStringLiteral("auto %1 = creator->get<%2>(%3);")
+ .arg(objectName, u"QQmlComponent"_s, creationIndexStr);
+ }
+ addObjectBinding(objectName);
+ *block << u"}"_s;
+ return;
+ }
+
+ const QString objectName = newSymbol(u"o"_s);
+ current.init.body << u"auto %1 = new %2(creator, engine, %3);"_s.arg(
+ objectName, object->internalName(), qobjectParent);
+ current.init.body << u"creator->set(%1, %2);"_s.arg(
+ QString::number(m_visitor->creationIndex(object)), objectName);
+
+ // refetch the same object during endInit to set the bindings
+ current.endInit.body << u"auto %1 = creator->get<%2>(%3);"_s.arg(
+ objectName, object->internalName(), QString::number(m_visitor->creationIndex(object)));
+ addObjectBinding(objectName);
+}
+
+/*!
+ * \internal
+ * Helper method used to keep compileBindingByType() readable.
+ */
+void QmltcCompiler::compileValueSourceOrInterceptorBinding(QmltcType &current,
+ const QQmlJSMetaPropertyBinding &binding,
+ const QQmlJSScope::ConstPtr &type,
+ const BindingAccessorData &accessor)
+{
+ Q_ASSERT(binding.bindingType() == QQmlSA::BindingType::ValueSource
+ || binding.bindingType() == QQmlSA::BindingType::Interceptor);
+
+ const QString &propertyName = binding.propertyName();
+ const QQmlJSMetaProperty property = type->property(propertyName);
+ QQmlJSScope::ConstPtr propertyType = property.type();
+
+ // NB: object is compiled with compileType(), here just need to use it
+ QSharedPointer<const QQmlJSScope> object;
+ if (binding.bindingType() == QQmlSA::BindingType::Interceptor)
+ object = binding.interceptorType();
+ else
+ object = binding.valueSourceType();
+
+ // Note: despite a binding being set for `accessor`, we use "this" as a
+ // parent of a created object. Both attached and grouped properties are
+ // parented by "this", so lifetime-wise we should be fine
+ const QString qobjectParent = u"this"_s;
+
+ if (!propertyType) {
+ recordError(binding.sourceLocation(),
+ u"Binding on property '" + propertyName + u"' of unknown type");
+ return;
+ }
+
+ auto &objectName = m_uniques[UniqueStringId(current, propertyName)].onAssignmentObjectName;
+ if (objectName.isEmpty()) {
+ objectName = u"onAssign_" + propertyName;
+
+ current.init.body << u"auto %1 = new %2(creator, engine, %3);"_s.arg(
+ objectName, object->internalName(), qobjectParent);
+ current.init.body << u"creator->set(%1, %2);"_s.arg(
+ QString::number(m_visitor->creationIndex(object)), objectName);
+
+ current.endInit.body << u"auto %1 = creator->get<%2>(%3);"_s.arg(
+ objectName, object->internalName(),
+ QString::number(m_visitor->creationIndex(object)));
+ }
+
+ // NB: we expect one "on" assignment per property, so creating
+ // QQmlProperty each time should be fine (unlike QQmlListReference)
+ current.endInit.body << u"{"_s;
+ current.endInit.body << u"QQmlProperty qmlprop(%1, %2);"_s.arg(
+ accessor.name, QQmlJSUtils::toLiteral(propertyName));
+ current.endInit.body << u"QT_PREPEND_NAMESPACE(QQmlCppOnAssignmentHelper)::set(%1, qmlprop);"_s
+ .arg(objectName);
+ current.endInit.body << u"}"_s;
+}
+
+/*!
+ * \internal
+ * Helper method used to keep compileBindingByType() readable.
+ */
+void QmltcCompiler::compileAttachedPropertyBinding(QmltcType &current,
+ const QQmlJSMetaPropertyBinding &binding,
+ const QQmlJSScope::ConstPtr &type,
+ const BindingAccessorData &accessor)
+{
+ Q_ASSERT(binding.bindingType() == QQmlSA::BindingType::AttachedProperty);
+
+ const QString &propertyName = binding.propertyName();
+ const QQmlJSMetaProperty property = type->property(propertyName);
+ QQmlJSScope::ConstPtr propertyType = property.type();
+
+ Q_ASSERT(accessor.name == u"this"_s); // doesn't have to hold, in fact
+ const auto attachedType = binding.attachingType();
+ Q_ASSERT(attachedType);
+
+ const QString attachingTypeName = propertyName; // acts as an identifier
+ auto attachingType = m_typeResolver->typeForName(attachingTypeName);
+
+ QString attachedTypeName = attachedType->baseTypeName();
+ Q_ASSERT(!attachedTypeName.isEmpty());
+
+ auto &attachedMemberName =
+ m_uniques[UniqueStringId(current, propertyName)].attachedVariableName;
+ if (attachedMemberName.isEmpty()) {
+ attachedMemberName = u"m_" + attachingTypeName;
+ // add attached type as a member variable to allow noop lookup
+ current.variables.emplaceBack(attachedTypeName + u" *", attachedMemberName, u"nullptr"_s);
+
+ if (propertyName == u"Component"_s) { // Component attached type is special
+ current.endInit.body << u"Q_ASSERT(qmlEngine(this));"_s;
+ current.endInit.body
+ << u"// attached Component must be added to the object's QQmlData"_s;
+ current.endInit.body
+ << u"Q_ASSERT(!QQmlEnginePrivate::get(qmlEngine(this))->activeObjectCreator);"_s;
+ }
+
+ // Note: getting attached property is fairly expensive
+ const QString getAttachedPropertyLine = u"qobject_cast<" + attachedTypeName
+ + u" *>(qmlAttachedPropertiesObject<" + attachingType->internalName()
+ + u">(this, /* create = */ true))";
+ current.endInit.body << attachedMemberName + u" = " + getAttachedPropertyLine + u";";
+
+ if (propertyName == u"Component"_s) {
+ // call completed/destruction signals appropriately
+ current.handleOnCompleted.body << u"Q_EMIT " + attachedMemberName + u"->completed();";
+ if (!current.dtor) {
+ current.dtor = QmltcDtor{};
+ current.dtor->name = u"~" + current.cppType;
+ }
+ current.dtor->body << u"Q_EMIT " + attachedMemberName + u"->destruction();";
+ }
+ }
+
+ auto subbindings = attachedType->ownPropertyBindingsInQmlIROrder();
+ // compile bindings of the attached property
+ partitionBindings(subbindings.begin(), subbindings.end());
+ compileBinding(current, subbindings.begin(), subbindings.end(), attachedType,
+ { type, attachedMemberName, propertyName, false });
+}
+
+/*!
+ * \internal
+ * Helper method used to keep compileBindingByType() readable.
+ */
+void QmltcCompiler::compileGroupPropertyBinding(QmltcType &current,
+ const QQmlJSMetaPropertyBinding &binding,
+ const QQmlJSScope::ConstPtr &type,
+ const BindingAccessorData &accessor)
+{
+ Q_ASSERT(binding.bindingType() == QQmlSA::BindingType::GroupProperty);
+
+ const QString &propertyName = binding.propertyName();
+ const QQmlJSMetaProperty property = type->property(propertyName);
+ QQmlJSScope::ConstPtr propertyType = property.type();
+
+ Q_ASSERT(accessor.name == u"this"_s); // doesn't have to hold, in fact
+ if (property.read().isEmpty()) {
+ recordError(binding.sourceLocation(),
+ u"READ function of group property '" + propertyName + u"' is unknown");
+ return;
+ }
+
+ auto groupType = binding.groupType();
+ Q_ASSERT(groupType);
+
+ const bool isValueType = propertyType->accessSemantics() == QQmlJSScope::AccessSemantics::Value;
+ if (!isValueType
+ && propertyType->accessSemantics() != QQmlJSScope::AccessSemantics::Reference) {
+ recordError(binding.sourceLocation(),
+ u"Group property '" + propertyName + u"' has unsupported access semantics");
+ return;
+ }
+
+ auto subbindings = groupType->ownPropertyBindingsInQmlIROrder();
+ auto firstScript = partitionBindings(subbindings.begin(), subbindings.end());
+
+ // if we have no non-script bindings, we have no bindings that affect
+ // the value type group, so no reason to generate the wrapping code
+ const bool generateValueTypeCode = isValueType && (subbindings.begin() != firstScript);
+
+ QString groupAccessor = QmltcCodeGenerator::wrap_privateClass(accessor.name, property) + u"->"
+ + property.read() + u"()";
+ // NB: used when isValueType == true
+ const QString groupPropertyVarName = accessor.name + u"_group_" + propertyName;
+ // value types are special
+ if (generateValueTypeCode) {
+ if (property.write().isEmpty()) { // just reject this
+ recordError(binding.sourceLocation(),
+ u"Group property '" + propertyName + u"' is a value type without a setter");
+ return;
+ }
+
+ current.endInit.body << u"auto " + groupPropertyVarName + u" = " + groupAccessor + u";";
+ // addressof operator is to make the binding logic work, which
+ // expects that `accessor.name` is a pointer type
+ groupAccessor = QmltcCodeGenerator::wrap_addressof(groupPropertyVarName);
+ }
+
+ // compile bindings of the grouped property
+ const auto compile = [&](const auto &bStart, const auto &bEnd) {
+ compileBinding(current, bStart, bEnd, groupType,
+ { type, groupAccessor, propertyName, isValueType });
+ };
+
+ auto it = subbindings.begin();
+ Q_ASSERT(std::all_of(it, firstScript, [](const auto &x) {
+ return x.bindingType() != QQmlSA::BindingType::Script;
+ }));
+ compile(it, firstScript);
+ it = firstScript;
+
+ // NB: script bindings are special on group properties. if our group is
+ // a value type, the binding would be installed on the *object* that
+ // holds the value type and not on the value type itself. this may cause
+ // subtle side issues (esp. when script binding is actually a simple
+ // enum value assignment - which is not recognized specially):
+ //
+ // auto valueTypeGroupProperty = getCopy();
+ // installBinding(valueTypeGroupProperty, "subproperty1"); // changes subproperty1 value
+ // setCopy(valueTypeGroupProperty); // oops, subproperty1 value changed to old again
+ if (generateValueTypeCode) { // write the value type back
+ current.endInit.body << QmltcCodeGenerator::wrap_privateClass(accessor.name, property)
+ + u"->" + property.write() + u"(" + groupPropertyVarName + u");";
+ }
+
+ // once the value is written back, process the script bindings
+ Q_ASSERT(std::all_of(it, subbindings.end(), [](const auto &x) {
+ return x.bindingType() == QQmlSA::BindingType::Script;
+ }));
+ compile(it, subbindings.end());
+}
+
+/*!
+ * \internal
+ * Helper method used to keep compileBindingByType() readable.
+ */
+void QmltcCompiler::compileTranslationBinding(QmltcType &current,
+ const QQmlJSMetaPropertyBinding &binding,
+ const QQmlJSScope::ConstPtr &type,
+ const BindingAccessorData &accessor)
+{
+ Q_ASSERT(binding.bindingType() == QQmlSA::BindingType::Translation
+ || binding.bindingType() == QQmlSA::BindingType::TranslationById);
+
+ const QString &propertyName = binding.propertyName();
+
+ auto [property, absoluteIndex] = getMetaPropertyIndex(type, propertyName);
+
+ if (absoluteIndex < 0) {
+ recordError(binding.sourceLocation(),
+ u"Binding on unknown property '" + propertyName + u"'");
+ return;
+ }
+
+ QString bindingTarget = accessor.name;
+
+ int valueTypeIndex = -1;
+ if (accessor.isValueType) {
+ Q_ASSERT(accessor.scope != type);
+ bindingTarget = u"this"_s; // TODO: not necessarily "this"?
+ auto [groupProperty, groupPropertyIndex] =
+ getMetaPropertyIndex(accessor.scope, accessor.propertyName);
+ if (groupPropertyIndex < 0) {
+ recordError(binding.sourceLocation(),
+ u"Binding on group property '" + accessor.propertyName
+ + u"' of unknown type");
+ return;
+ }
+ valueTypeIndex = absoluteIndex;
+ absoluteIndex = groupPropertyIndex; // e.g. index of accessor.name
+ }
+
+ QmltcCodeGenerator::TranslationBindingInfo info;
+ info.unitVarName = generate_callCompilationUnit(m_urlMethodName);
+ info.scope = u"this"_s;
+ info.target = u"this"_s;
+ info.propertyIndex = absoluteIndex;
+ info.property = property;
+ info.data = binding.translationDataValue(m_url);
+ info.valueTypeIndex = valueTypeIndex;
+ info.line = binding.sourceLocation().startLine;
+ info.column = binding.sourceLocation().startColumn;
+
+ QmltcCodeGenerator::generate_createTranslationBindingOnProperty(&current.endInit.body, info);
+}
+
+void QmltcCompiler::processLastListBindings(QmltcType &current, const QQmlJSScope::ConstPtr &type,
+ const BindingAccessorData &accessor)
+{
+ if (unprocessedListBindings.empty())
+ return;
+
+ QmltcCodeGenerator::generate_assignToListProperty(
+ &current.endInit.body, type, unprocessedListProperty, unprocessedListBindings,
+ accessor.name,
+ m_uniques[UniqueStringId(current, unprocessedListProperty.propertyName())]
+ .qmlListVariableName);
+
+ unprocessedListBindings.clear();
+}
+
+void QmltcCompiler::compileBinding(QmltcType &current,
+ QList<QQmlJSMetaPropertyBinding>::iterator bindingStart,
+ QList<QQmlJSMetaPropertyBinding>::iterator bindingEnd,
+ const QQmlJSScope::ConstPtr &type,
+ const BindingAccessorData &accessor)
+{
+ for (auto it = bindingStart; it != bindingEnd; it++) {
+ const QQmlJSMetaPropertyBinding &binding = *it;
+ const QString &propertyName = binding.propertyName();
+ Q_ASSERT(!propertyName.isEmpty());
+
+ // Note: unlike QQmlObjectCreator, we don't have to do a complicated
+ // deferral logic for bindings: if a binding is deferred, it is not compiled
+ // (potentially, with all the bindings inside of it), period.
+ if (type->isNameDeferred(propertyName)) {
+ const auto location = binding.sourceLocation();
+ // make sure group property is not generalized by checking if type really has a property
+ // called propertyName. If not, it is probably an id.
+ if (binding.bindingType() == QQmlSA::BindingType::GroupProperty
+ && type->hasProperty(propertyName)) {
+ qCWarning(lcQmltcCompiler)
+ << QStringLiteral("Binding at line %1 column %2 is not deferred as it is a "
+ "binding on a group property.")
+ .arg(QString::number(location.startLine),
+ QString::number(location.startColumn));
+ // we do not support PropertyChanges and other types with similar
+ // behavior yet, so this binding is compiled
+ } else {
+ qCDebug(lcQmltcCompiler)
+ << QStringLiteral(
+ "Binding at line %1 column %2 is deferred and thus not compiled")
+ .arg(QString::number(location.startLine),
+ QString::number(location.startColumn));
+ continue;
+ }
+ }
+
+ const QQmlJSMetaProperty metaProperty = type->property(propertyName);
+ const QQmlJSScope::ConstPtr propertyType = metaProperty.type();
+
+ if (!(qIsReferenceTypeList(metaProperty) && unprocessedListProperty == metaProperty)) {
+ processLastListBindings(current, type, accessor);
+ }
+
+ compileBindingByType(current, binding, type, accessor);
+ }
+
+ processLastListBindings(current, type, accessor);
+}
+
+void QmltcCompiler::compileBindingByType(QmltcType &current,
+ const QQmlJSMetaPropertyBinding &binding,
+ const QQmlJSScope::ConstPtr &type,
+ const BindingAccessorData &accessor)
+{
+ const QString &propertyName = binding.propertyName();
+ const QQmlJSMetaProperty metaProperty = type->property(propertyName);
+ const QQmlJSScope::ConstPtr propertyType = metaProperty.type();
+
+ const auto assignToProperty = [&](const QQmlJSMetaProperty &p, const QString &value,
+ bool constructFromQObject = false) {
+ QmltcCodeGenerator::generate_assignToProperty(&current.endInit.body, type, p, value,
+ accessor.name, constructFromQObject);
+ };
+ switch (binding.bindingType()) {
+ case QQmlSA::BindingType::BoolLiteral: {
+ const bool value = binding.boolValue();
+ assignToProperty(metaProperty, value ? u"true"_s : u"false"_s);
+ break;
+ }
+ case QQmlSA::BindingType::NumberLiteral: {
+ assignToProperty(metaProperty, QString::number(binding.numberValue()));
+ break;
+ }
+ case QQmlSA::BindingType::StringLiteral: {
+ QString value = QQmlJSUtils::toLiteral(binding.stringValue());
+ if (auto type = metaProperty.type()) {
+ if (type->internalName() == u"QUrl"_s) {
+ value = u"QUrl(%1)"_s.arg(value);
+ }
+ }
+ assignToProperty(metaProperty, value);
+ break;
+ }
+ case QQmlSA::BindingType::RegExpLiteral: {
+ const QString value =
+ u"QRegularExpression(%1)"_s.arg(QQmlJSUtils::toLiteral(binding.regExpValue()));
+ assignToProperty(metaProperty, value);
+ break;
+ }
+ case QQmlSA::BindingType::Null: {
+ // poor check: null bindings are only supported for var and objects
+ Q_ASSERT(propertyType->isSameType(m_typeResolver->varType())
+ || propertyType->accessSemantics() == QQmlJSScope::AccessSemantics::Reference);
+ if (propertyType->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
+ assignToProperty(metaProperty, u"nullptr"_s);
+ else
+ assignToProperty(metaProperty, u"QVariant::fromValue(nullptr)"_s);
+ break;
+ }
+ case QQmlSA::BindingType::Script: {
+ QString bindingSymbolName = type->internalName() + u'_' + propertyName + u"_binding";
+ bindingSymbolName.replace(u'.', u'_'); // can happen with group properties
+ compileScriptBinding(current, binding, bindingSymbolName, type, propertyName, propertyType,
+ accessor);
+ break;
+ }
+ case QQmlSA::BindingType::Object: {
+ compileObjectBinding(current, binding, type, accessor);
+ break;
+ }
+ case QQmlSA::BindingType::Interceptor:
+ Q_FALLTHROUGH();
+ case QQmlSA::BindingType::ValueSource: {
+ compileValueSourceOrInterceptorBinding(current, binding, type, accessor);
+ break;
+ }
+ case QQmlSA::BindingType::AttachedProperty: {
+ compileAttachedPropertyBinding(current, binding, type, accessor);
+ break;
+ }
+ case QQmlSA::BindingType::GroupProperty: {
+ compileGroupPropertyBinding(current, binding, type, accessor);
+ break;
+ }
+
+ case QQmlSA::BindingType::TranslationById:
+ case QQmlSA::BindingType::Translation: {
+ compileTranslationBinding(current, binding, type, accessor);
+ break;
+ }
+ case QQmlSA::BindingType::Invalid: {
+ recordError(binding.sourceLocation(), u"This binding is invalid"_s);
+ break;
+ }
+ default: {
+ recordError(binding.sourceLocation(), u"Binding is not supported"_s);
+ break;
+ }
+ }
+}
+
+// returns compiled script binding for "property changed" handler in a form of object type
+static QmltcType compileScriptBindingPropertyChangeHandler(const QQmlJSMetaPropertyBinding &binding,
+ const QQmlJSScope::ConstPtr &objectType,
+ const QString &urlMethodName,
+ const QString &functorCppType,
+ const QString &objectCppType)
+{
+ QmltcType bindingFunctor {};
+ bindingFunctor.cppType = functorCppType;
+ bindingFunctor.ignoreInit = true;
+
+ // default member variable and ctor:
+ const QString pointerToObject = objectCppType + u" *";
+ bindingFunctor.variables.emplaceBack(
+ QmltcVariable { pointerToObject, u"m_self"_s, u"nullptr"_s });
+ bindingFunctor.baselineCtor.name = functorCppType;
+ bindingFunctor.baselineCtor.parameterList.emplaceBack(
+ QmltcVariable { pointerToObject, u"self"_s, QString() });
+ bindingFunctor.baselineCtor.initializerList.emplaceBack(u"m_self(self)"_s);
+
+ // call operator:
+ QmltcMethod callOperator {};
+ callOperator.returnType = u"void"_s;
+ callOperator.name = u"operator()"_s;
+ callOperator.modifiers << u"const"_s;
+ QmltcCodeGenerator::generate_callExecuteRuntimeFunction(
+ &callOperator.body, urlMethodName + u"()",
+ objectType->ownRuntimeFunctionIndex(binding.scriptIndex()), u"m_self"_s, u"void"_s, {});
+
+ bindingFunctor.functions.emplaceBack(std::move(callOperator));
+
+ return bindingFunctor;
+}
+
+// finds property for given scope and returns it together with the absolute
+// property index in the property array of the corresponding QMetaObject
+static std::pair<QQmlJSMetaProperty, int> getMetaPropertyIndex(const QQmlJSScope::ConstPtr &scope,
+ const QString &propertyName)
+{
+ auto owner = QQmlJSScope::ownerOfProperty(scope, propertyName).scope;
+ Q_ASSERT(owner);
+ const QQmlJSMetaProperty p = owner->ownProperty(propertyName);
+ if (!p.isValid())
+ return { p, -1 };
+ int index = p.index();
+ if (index < 0) // this property doesn't have index - comes from QML
+ return { p, -1 };
+
+ const auto increment = [&](const QQmlJSScope::ConstPtr &type, QQmlJSScope::ExtensionKind m) {
+ // owner of property is not included in the offset calculation (relative
+ // index is already added as p.index())
+ if (type->isSameType(owner))
+ return;
+
+ // extension namespace and JavaScript properties are ignored
+ if (m == QQmlJSScope::ExtensionNamespace || m == QQmlJSScope::ExtensionJavaScript)
+ return;
+
+ index += int(type->ownProperties().size());
+ };
+ QQmlJSUtils::traverseFollowingMetaObjectHierarchy(scope, owner, increment);
+ return { p, index };
+}
+
+void QmltcCompiler::compileScriptBinding(QmltcType &current,
+ const QQmlJSMetaPropertyBinding &binding,
+ const QString &bindingSymbolName,
+ const QQmlJSScope::ConstPtr &objectType,
+ const QString &propertyName,
+ const QQmlJSScope::ConstPtr &propertyType,
+ const QmltcCompiler::BindingAccessorData &accessor)
+{
+ const auto compileScriptSignal = [&](const QString &name) {
+ QString This_signal = u"this"_s;
+ QString This_slot = u"this"_s;
+ QString objectClassName_signal = objectType->internalName();
+ QString objectClassName_slot = objectType->internalName();
+
+ // TODO: ugly crutch to make stuff work
+ if (accessor.name != u"this"_s) { // e.g. if attached property
+ This_signal = accessor.name;
+ This_slot = u"this"_s; // still
+ objectClassName_signal = objectType->baseTypeName();
+ objectClassName_slot = current.cppType; // real base class where slot would go
+ }
+ Q_ASSERT(!objectClassName_signal.isEmpty());
+ Q_ASSERT(!objectClassName_slot.isEmpty());
+
+ const auto signalMethods = objectType->methods(name, QQmlJSMetaMethodType::Signal);
+ Q_ASSERT(!signalMethods.isEmpty()); // an error somewhere else
+ QQmlJSMetaMethod signal = signalMethods.at(0);
+ Q_ASSERT(signal.methodType() == QQmlJSMetaMethodType::Signal);
+
+ const QString signalName = signal.methodName();
+ const QString slotName = newSymbol(signalName + u"_slot");
+
+ const QString signalReturnType = figureReturnType(signal);
+ const QList<QmltcVariable> slotParameters =
+ compileMethodParameters(signal.parameters(), /* allow unnamed = */ true);
+
+ // SignalHander specific:
+ QmltcMethod slotMethod {};
+ slotMethod.returnType = signalReturnType;
+ slotMethod.name = slotName;
+ slotMethod.parameterList = slotParameters;
+
+ QmltcCodeGenerator::generate_callExecuteRuntimeFunction(
+ &slotMethod.body, m_urlMethodName + u"()",
+ objectType->ownRuntimeFunctionIndex(binding.scriptIndex()),
+ u"this"_s, // Note: because script bindings always use current QML object scope
+ signalReturnType, slotParameters);
+ slotMethod.type = QQmlJSMetaMethodType::Slot;
+
+ current.functions << std::move(slotMethod);
+ current.setComplexBindings.body << u"QObject::connect(" + This_signal + u", " + u"&"
+ + objectClassName_signal + u"::" + signalName + u", " + This_slot + u", &"
+ + objectClassName_slot + u"::" + slotName + u");";
+ };
+
+ switch (binding.scriptKind()) {
+ case QQmlSA::ScriptBindingKind::PropertyBinding: {
+ if (!propertyType) {
+ recordError(binding.sourceLocation(),
+ u"Binding on property '" + propertyName + u"' of unknown type");
+ return;
+ }
+
+ auto [property, absoluteIndex] = getMetaPropertyIndex(objectType, propertyName);
+ if (absoluteIndex < 0) {
+ recordError(binding.sourceLocation(),
+ u"Binding on unknown property '" + propertyName + u"'");
+ return;
+ }
+
+ QString bindingTarget = accessor.name;
+
+ int valueTypeIndex = -1;
+ if (accessor.isValueType) {
+ Q_ASSERT(accessor.scope != objectType);
+ bindingTarget = u"this"_s; // TODO: not necessarily "this"?
+ auto [groupProperty, groupPropertyIndex] =
+ getMetaPropertyIndex(accessor.scope, accessor.propertyName);
+ if (groupPropertyIndex < 0) {
+ recordError(binding.sourceLocation(),
+ u"Binding on group property '" + accessor.propertyName
+ + u"' of unknown type");
+ return;
+ }
+ valueTypeIndex = absoluteIndex;
+ absoluteIndex = groupPropertyIndex; // e.g. index of accessor.name
+ }
+
+ QmltcCodeGenerator::generate_createBindingOnProperty(
+ &current.setComplexBindings.body, generate_callCompilationUnit(m_urlMethodName),
+ u"this"_s, // NB: always using enclosing object as a scope for the binding
+ static_cast<qsizetype>(objectType->ownRuntimeFunctionIndex(binding.scriptIndex())),
+ bindingTarget, // binding target
+ // value types are special and are bound through valueTypeIndex
+ accessor.isValueType ? QQmlJSScope::ConstPtr() : objectType, absoluteIndex,
+ property, valueTypeIndex, accessor.name);
+ break;
+ }
+ case QQmlSA::ScriptBindingKind::SignalHandler: {
+ const auto name = QQmlSignalNames::handlerNameToSignalName(propertyName);
+ Q_ASSERT(name.has_value());
+ compileScriptSignal(*name);
+ break;
+ }
+ case QQmlSA ::ScriptBindingKind::ChangeHandler: {
+ const QString objectClassName = objectType->internalName();
+ const QString bindingFunctorName = newSymbol(bindingSymbolName + u"Functor");
+
+ const auto signalName = QQmlSignalNames::handlerNameToSignalName(propertyName);
+ Q_ASSERT(signalName.has_value()); // an error somewhere else
+ const auto actualProperty =
+ QQmlJSUtils::propertyFromChangedHandler(objectType, propertyName);
+ Q_ASSERT(actualProperty.has_value()); // an error somewhere else
+ const auto actualPropertyType = actualProperty->type();
+ if (!actualPropertyType) {
+ recordError(binding.sourceLocation(),
+ u"Binding on property '" + actualProperty->propertyName()
+ + u"' of unknown type");
+ return;
+ }
+
+ // due to historical reasons (QQmlObjectCreator), prefer NOTIFY over
+ // BINDABLE when both are available. thus, test for notify first
+ const QString notifyString = actualProperty->notify();
+ if (!notifyString.isEmpty()) {
+ compileScriptSignal(notifyString);
+ break;
+ }
+ const QString bindableString = actualProperty->bindable();
+ QString typeOfQmlBinding =
+ u"std::unique_ptr<QPropertyChangeHandler<" + bindingFunctorName + u">>";
+
+ current.children << compileScriptBindingPropertyChangeHandler(
+ binding, objectType, m_urlMethodName, bindingFunctorName, objectClassName);
+
+ current.setComplexBindings.body << u"if (!%1.contains(\"%2\"))"_s.arg(
+ current.propertyInitializer.initializedCache.name, propertyName);
+
+ // TODO: this could be dropped if QQmlEngine::setContextForObject() is
+ // done before currently generated C++ object is constructed
+ current.setComplexBindings.body << u" "_s + bindingSymbolName + u".reset(new QPropertyChangeHandler<"
+ + bindingFunctorName + u">("
+ + QmltcCodeGenerator::wrap_privateClass(accessor.name, *actualProperty)
+ + u"->" + bindableString + u"().onValueChanged(" + bindingFunctorName + u"("
+ + accessor.name + u"))));";
+
+ current.variables.emplaceBack(
+ QmltcVariable { typeOfQmlBinding, bindingSymbolName, QString() });
+ break;
+ }
+ default:
+ recordError(binding.sourceLocation(), u"Invalid script binding found"_s);
+ break;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qmltc/qmltccompiler.h b/tools/qmltc/qmltccompiler.h
new file mode 100644
index 0000000000..3deab6d44e
--- /dev/null
+++ b/tools/qmltc/qmltccompiler.h
@@ -0,0 +1,206 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef QMLTCCOMPILER_H
+#define QMLTCCOMPILER_H
+
+#include "qmltctyperesolver.h"
+#include "qmltcvisitor.h"
+#include "qmltcoutputir.h"
+
+#include <QtCore/qcommandlineparser.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qhash.h>
+
+#include <private/qqmljslogger_p.h>
+
+#include <memory>
+
+QT_BEGIN_NAMESPACE
+
+struct QmltcCompilerInfo
+{
+ QString outputCppFile;
+ QString outputHFile;
+ QString outputNamespace;
+ QString resourcePath;
+ QString exportMacro;
+ QString exportInclude;
+};
+
+class QmltcCompiler
+{
+ using InlineComponentOrDocumentRootName = QQmlJSScope::InlineComponentOrDocumentRootName;
+ using InlineComponentNameType = QQmlJSScope::InlineComponentNameType;
+ using RootDocumentNameType = QQmlJSScope::RootDocumentNameType;
+
+public:
+ QmltcCompiler(const QString &url, QmltcTypeResolver *resolver, QmltcVisitor *visitor,
+ QQmlJSLogger *logger);
+ void compile(const QmltcCompilerInfo &info);
+
+ ~QmltcCompiler();
+
+ /*! \internal
+
+ Returns \c true if \a binding is considered complex by the compiler
+ (requires special code generation)
+ */
+ static bool isComplexBinding(const QQmlJSMetaPropertyBinding &binding)
+ {
+ // TODO: translation bindings (once supported) are also complex?
+ return binding.bindingType() == QQmlSA::BindingType::Script;
+ }
+
+private:
+ QString m_url; // QML input file url
+ QmltcTypeResolver *m_typeResolver = nullptr;
+ QmltcVisitor *m_visitor = nullptr;
+ QQmlJSLogger *m_logger = nullptr;
+ QmltcCompilerInfo m_info {}; // miscellaneous input/output information
+ QString m_urlMethodName;
+
+ struct UniqueStringId;
+ struct QmltcTypeLocalData;
+ // per-type, per-property code generation cache of created symbols
+ QHash<UniqueStringId, QmltcTypeLocalData> m_uniques;
+
+ void compileUrlMethod(QmltcMethod &urlMethod, const QString &urlMethodName);
+ void
+ compileType(QmltcType &current, const QQmlJSScope::ConstPtr &type,
+ std::function<void(QmltcType &, const QQmlJSScope::ConstPtr &)> compileElements);
+ void compileTypeElements(QmltcType &current, const QQmlJSScope::ConstPtr &type);
+ void compileEnum(QmltcType &current, const QQmlJSMetaEnum &e);
+ void compileMethod(QmltcType &current, const QQmlJSMetaMethod &m,
+ const QQmlJSScope::ConstPtr &owner);
+ void compileProperty(QmltcType &current, const QQmlJSMetaProperty &p,
+ const QQmlJSScope::ConstPtr &owner);
+ void compileAlias(QmltcType &current, const QQmlJSMetaProperty &alias,
+ const QQmlJSScope::ConstPtr &owner);
+ void compileExtraListMethods(QmltcType &current, const QQmlJSMetaProperty &p);
+
+ /*!
+ \internal
+
+ Helper structure that holds the information necessary for most bindings,
+ such as accessor name, which is used to reference the properties. For
+ example:
+ > (accessor.name)->(propertyName) results in "this->myProperty"
+
+ This data is also used in more advanced scenarios by attached and
+ grouped properties
+ */
+ struct BindingAccessorData
+ {
+ QQmlJSScope::ConstPtr scope; // usually the current type
+ QString name = QStringLiteral("this");
+ QString propertyName = QString();
+ bool isValueType = false;
+ };
+
+ QStringList unprocessedListBindings;
+ QQmlJSMetaProperty unprocessedListProperty;
+
+ void processLastListBindings(QmltcType &current, const QQmlJSScope::ConstPtr &type,
+ const BindingAccessorData &accessor);
+
+ void compileBinding(QmltcType &current, QList<QQmlJSMetaPropertyBinding>::iterator bindingStart,
+ QList<QQmlJSMetaPropertyBinding>::iterator bindingEnd,
+ const QQmlJSScope::ConstPtr &type, const BindingAccessorData &accessor);
+
+ void compileBindingByType(QmltcType &current, const QQmlJSMetaPropertyBinding &binding,
+ const QQmlJSScope::ConstPtr &type,
+ const BindingAccessorData &accessor);
+
+ void compileObjectBinding(QmltcType &current, const QQmlJSMetaPropertyBinding &binding,
+ const QQmlJSScope::ConstPtr &type,
+ const BindingAccessorData &accessor);
+
+ void compileValueSourceOrInterceptorBinding(QmltcType &current,
+ const QQmlJSMetaPropertyBinding &binding,
+ const QQmlJSScope::ConstPtr &type,
+ const BindingAccessorData &accessor);
+
+ void compileAttachedPropertyBinding(QmltcType &current,
+ const QQmlJSMetaPropertyBinding &binding,
+ const QQmlJSScope::ConstPtr &type,
+ const BindingAccessorData &accessor);
+
+ void compileGroupPropertyBinding(QmltcType &current, const QQmlJSMetaPropertyBinding &binding,
+ const QQmlJSScope::ConstPtr &type,
+ const BindingAccessorData &accessor);
+
+ void compileTranslationBinding(QmltcType &current, const QQmlJSMetaPropertyBinding &binding,
+ const QQmlJSScope::ConstPtr &type,
+ const BindingAccessorData &accessor);
+
+ // special case (for simplicity)
+ void compileScriptBinding(QmltcType &current, const QQmlJSMetaPropertyBinding &binding,
+ const QString &bindingSymbolName, const QQmlJSScope::ConstPtr &type,
+ const QString &propertyName,
+ const QQmlJSScope::ConstPtr &propertyType,
+ const BindingAccessorData &accessor);
+
+ /*!
+ \internal
+ Helper structure that acts as a key in a hash-table of
+ QmltcType-specific data (such as local variable names). Using a
+ hash-table allows to avoid creating the same variables multiple times
+ during binding compilation, which leads to better code generation and
+ faster object creation. This is really something that the QML optimizer
+ should do, but we have only this home-grown alternative at the moment
+ */
+ struct UniqueStringId
+ {
+ QString unique;
+ UniqueStringId(const QmltcType &context, const QString &property)
+ : unique(context.cppType + u"_" + property) // this is unique enough
+ {
+ Q_ASSERT(!context.cppType.isEmpty());
+ Q_ASSERT(!property.isEmpty());
+ }
+ friend bool operator==(const UniqueStringId &x, const UniqueStringId &y)
+ {
+ return x.unique == y.unique;
+ }
+ friend bool operator!=(const UniqueStringId &x, const UniqueStringId &y)
+ {
+ return !(x == y);
+ }
+ friend size_t qHash(const UniqueStringId &x, size_t seed = 0)
+ {
+ return qHash(x.unique, seed);
+ }
+ };
+
+ struct QmltcTypeLocalData
+ {
+ // empty QString() means that the local data is not present (yet)
+ QString qmlListVariableName;
+ QString onAssignmentObjectName;
+ QString attachedVariableName;
+ };
+
+ QHash<QString, qsizetype> m_symbols;
+ QString newSymbol(const QString &base);
+
+ bool hasErrors() const { return m_logger->hasErrors(); }
+ void recordError(const QQmlJS::SourceLocation &location, const QString &message,
+ QQmlJS::LoggerWarningId id = qmlCompiler)
+ {
+ // pretty much any compiler error is a critical error (we cannot
+ // generate code - compilation fails)
+ m_logger->log(message, id, location);
+ }
+ void recordError(const QV4::CompiledData::Location &location, const QString &message,
+ QQmlJS::LoggerWarningId id = qmlCompiler)
+ {
+ recordError(QQmlJS::SourceLocation { 0, 0, location.line(), location.column() }, message,
+ id);
+ }
+};
+
+QT_END_NAMESPACE
+
+#endif // QMLTCCOMPILER_H
diff --git a/tools/qmltc/qmltccompilerpieces.cpp b/tools/qmltc/qmltccompilerpieces.cpp
new file mode 100644
index 0000000000..cd1735bc07
--- /dev/null
+++ b/tools/qmltc/qmltccompilerpieces.cpp
@@ -0,0 +1,352 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "qmltccompilerpieces.h"
+
+#include <private/qqmljsutils_p.h>
+
+#include <tuple>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+static QString scopeName(const QQmlJSScope::ConstPtr &scope)
+{
+ Q_ASSERT(scope->isFullyResolved());
+ const auto scopeType = scope->scopeType();
+ if (scopeType == QQmlSA::ScopeType::GroupedPropertyScope
+ || scopeType == QQmlSA::ScopeType::AttachedPropertyScope) {
+ return scope->baseType()->internalName();
+ }
+ return scope->internalName();
+}
+
+QmltcCodeGenerator::PreparedValue
+QmltcCodeGenerator::wrap_extensionType(const QQmlJSScope::ConstPtr &type,
+ const QQmlJSMetaProperty &p, const QString &accessor)
+{
+ Q_ASSERT(type->isFullyResolved());
+
+ QStringList prologue;
+ QString value = accessor;
+ QStringList epilogue;
+
+ auto [owner, ownerKind] = QQmlJSScope::ownerOfProperty(type, p.propertyName());
+ Q_ASSERT(owner);
+ Q_ASSERT(owner->isFullyResolved());
+
+ // properties are only visible when we use QML_{NAMESPACE_}EXTENDED
+ if (ownerKind == QQmlJSScope::ExtensionType) {
+ // extensions is a C++-only feature:
+ Q_ASSERT(!owner->isComposite());
+
+ // have to wrap the property into an extension, but we need to figure
+ // out whether the type is QObject-based or not
+ prologue << u"{"_s;
+ const QString extensionObjectName = u"extObject"_s;
+ if (type->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
+ // we have a Q_OBJECT. in this case, we call qmlExtendedObject()
+ // function that should return us the extension object. for that we
+ // have to figure out which specific extension we want here
+
+ int extensionIndex = 0;
+ auto cppBase = QQmlJSScope::nonCompositeBaseType(type);
+ for (auto t = cppBase; t; t = t->baseType()) {
+ if (auto [ext, kind] = t->extensionType(); kind != QQmlJSScope::NotExtension) {
+ if (ext->isSameType(owner))
+ break;
+ ++extensionIndex;
+ }
+ }
+
+ prologue << u"static_assert(std::is_base_of<%1, %2>::value);"_s.arg(u"QObject"_s,
+ scopeName(type));
+ prologue << u"auto %1 = qobject_cast<%2 *>(QQmlPrivate::qmlExtendedObject(%3, %4));"_s
+ .arg(extensionObjectName, owner->internalName(), accessor,
+ QString::number(extensionIndex));
+ } else {
+ // we have a Q_GADGET. the assumption for extension types is that we
+ // can reinterpret_cast a Q_GADGET object into an extension type
+ // object and then interact with the extension object right away
+ prologue << u"static_assert(sizeof(%1) == sizeof(%2));"_s.arg(scopeName(type),
+ owner->internalName());
+ prologue << u"static_assert(alignof(%1) == alignof(%2));"_s.arg(scopeName(type),
+ owner->internalName());
+ prologue << u"auto %1 = reinterpret_cast<%2 *>(%3);"_s.arg(
+ extensionObjectName, owner->internalName(), accessor);
+ }
+ prologue << u"Q_ASSERT(%1);"_s.arg(extensionObjectName);
+ value = extensionObjectName;
+ epilogue << u"}"_s;
+ }
+
+ return { prologue, value, epilogue };
+}
+
+void QmltcCodeGenerator::generate_assignToListProperty(
+ QStringList *block, const QQmlJSScope::ConstPtr &type, const QQmlJSMetaProperty &p,
+ const QStringList &values, const QString &accessor, QString &qmlListVarName)
+{
+ Q_UNUSED(type); // might be needed
+ const bool populateLocalListProperty = qmlListVarName.isEmpty();
+
+ if (populateLocalListProperty) {
+ auto [extensionPrologue, extensionAccessor, extensionEpilogue] =
+ QmltcCodeGenerator::wrap_extensionType(
+ type, p, QmltcCodeGenerator::wrap_privateClass(accessor, p));
+
+ qmlListVarName = u"listprop_%1"_s.arg(p.propertyName());
+ QQmlJSScope::ConstPtr valueType = p.type()->valueType();
+ *block << u"QQmlListProperty<%1> %2;"_s.arg(valueType->internalName(), qmlListVarName);
+ *block << extensionPrologue;
+ *block << u"%1 = %2->%3();"_s.arg(qmlListVarName, extensionAccessor, p.read());
+ *block << extensionEpilogue;
+ }
+ for (const QString &value : values) {
+ auto [prologue, wrappedValue, epilogue] =
+ QmltcCodeGenerator::wrap_mismatchingTypeConversion(p, value);
+ *block << prologue;
+ *block << u"%1.append(std::addressof(%1), %2);"_s.arg(qmlListVarName, wrappedValue);
+ *block << epilogue;
+ }
+}
+
+void QmltcCodeGenerator::generate_assignToProperty(QStringList *block,
+ const QQmlJSScope::ConstPtr &type,
+ const QQmlJSMetaProperty &p,
+ const QString &value, const QString &accessor,
+ bool constructFromQObject)
+{
+ Q_ASSERT(block);
+ Q_ASSERT(p.isValid());
+ Q_ASSERT(!p.isList()); // NB: this code does not handle list properties
+
+ const QString propertyName = p.propertyName();
+
+ if (type->hasOwnProperty(p.propertyName()) && !p.isAlias()) {
+ Q_ASSERT(!p.isPrivate());
+ // this object is compiled, so just assignment should work fine
+ auto [prologue, wrappedValue, epilogue] =
+ QmltcCodeGenerator::wrap_mismatchingTypeConversion(p, value);
+ *block += prologue;
+ *block << u"%1->m_%2 = %3;"_s.arg(accessor, propertyName, wrappedValue);
+ *block += epilogue;
+ } else if (QString propertySetter = p.write(); !propertySetter.isEmpty()
+ && !QQmlJSUtils::bindablePropertyHasDefaultAccessor(
+ p, QQmlJSUtils::PropertyAccessor_Write)) {
+ // there's a WRITE function
+ auto [prologue, wrappedValue, epilogue] =
+ QmltcCodeGenerator::wrap_mismatchingTypeConversion(p, value);
+ *block += prologue;
+
+ auto [extensionPrologue, extensionAccessor, extensionEpilogue] =
+ QmltcCodeGenerator::wrap_extensionType(
+ type, p, QmltcCodeGenerator::wrap_privateClass(accessor, p));
+ *block += extensionPrologue;
+ *block << extensionAccessor + u"->" + propertySetter + u"(" + wrappedValue + u");";
+ *block += extensionEpilogue;
+
+ *block += epilogue;
+ } else {
+ // this property is weird, fallback to `setProperty`
+ *block << u"{ // couldn't find property setter, so using QObject::setProperty()"_s;
+ QString val = value;
+ if (constructFromQObject) {
+ const QString variantName = u"var_" + propertyName;
+ *block << u"QVariant " + variantName + u";";
+ *block << variantName + u".setValue(" + val + u");";
+ val = u"std::move(" + variantName + u")";
+ }
+ // NB: setProperty() would handle private properties
+ *block << accessor + u"->setProperty(\"" + propertyName + u"\", " + val + u");";
+ *block << u"}"_s;
+ }
+}
+
+void QmltcCodeGenerator::generate_setIdValue(QStringList *block, const QString &context,
+ qsizetype index, const QString &accessor,
+ const QString &idString)
+{
+ Q_ASSERT(index >= 0);
+ *block << u"Q_ASSERT(%1 < %2->numIdValues()); // make sure Id is in bounds"_s.arg(index).arg(
+ context);
+ *block << u"%1->setIdValue(%2 /* id: %3 */, %4);"_s.arg(context, QString::number(index),
+ idString, accessor);
+}
+
+void QmltcCodeGenerator::generate_callExecuteRuntimeFunction(
+ QStringList *block, const QString &url, QQmlJSMetaMethod::AbsoluteFunctionIndex index,
+ const QString &accessor, const QString &returnType, const QList<QmltcVariable> &parameters)
+{
+ *block << u"QQmlEnginePrivate *e = QQmlEnginePrivate::get(qmlEngine(" + accessor + u"));";
+
+ const QString returnValueName = u"_ret"_s;
+ QStringList args;
+ args.reserve(parameters.size() + 1);
+ QStringList types;
+ types.reserve(parameters.size() + 1);
+ if (returnType == u"void"_s) {
+ args << u"nullptr"_s;
+ types << u"QMetaType::fromType<void>()"_s;
+ } else {
+ *block << returnType + u" " + returnValueName + u"{};"; // TYPE _ret{};
+ args << u"const_cast<void *>(reinterpret_cast<const void *>(std::addressof("
+ + returnValueName + u")))";
+ types << u"QMetaType::fromType<std::decay_t<" + returnType + u">>()";
+ }
+
+ for (const QmltcVariable &p : parameters) {
+ args << u"const_cast<void *>(reinterpret_cast<const void *>(std::addressof(" + p.name
+ + u")))";
+ types << u"QMetaType::fromType<std::decay_t<" + p.cppType + u">>()";
+ }
+
+ *block << u"void *_a[] = { " + args.join(u", "_s) + u" };";
+ *block << u"QMetaType _t[] = { " + types.join(u", "_s) + u" };";
+ const qsizetype runtimeIndex = static_cast<qsizetype>(index);
+ Q_ASSERT(runtimeIndex >= 0);
+ *block << u"e->executeRuntimeFunction(" + url + u", " + QString::number(runtimeIndex) + u", "
+ + accessor + u", " + QString::number(parameters.size()) + u", _a, _t);";
+ if (returnType != u"void"_s)
+ *block << u"return " + returnValueName + u";";
+}
+
+void QmltcCodeGenerator::generate_createBindingOnProperty(
+ QStringList *block, const QString &unitVarName, const QString &scope,
+ qsizetype functionIndex, const QString &target, const QQmlJSScope::ConstPtr &targetType,
+ int propertyIndex, const QQmlJSMetaProperty &p, int valueTypeIndex,
+ const QString &subTarget)
+{
+ const QString propName = QQmlJSUtils::toLiteral(p.propertyName());
+ if (QString bindable = p.bindable(); !bindable.isEmpty()) {
+ // TODO: test that private properties are bindable
+ QString createBindingForBindable = u"QT_PREPEND_NAMESPACE(QQmlCppBinding)::"
+ u"createBindingForBindable("
+ + unitVarName + u", " + scope + u", " + QString::number(functionIndex) + u", "
+ + target + u", " + QString::number(propertyIndex) + u", "
+ + QString::number(valueTypeIndex) + u", " + propName + u")";
+ const QString accessor = (valueTypeIndex == -1) ? target : subTarget;
+
+ QStringList prologue;
+ QString value = QmltcCodeGenerator::wrap_privateClass(accessor, p);
+ QStringList epilogue;
+ if (targetType) {
+ auto [pro, v, epi] = QmltcCodeGenerator::wrap_extensionType(targetType, p, value);
+ std::tie(prologue, value, epilogue) = std::make_tuple(pro, v, epi);
+ }
+
+ *block += prologue;
+ *block << u"if (!initializedCache.contains(\"%1\"))"_s.arg(p.propertyName());
+ *block << u" "_s + value + u"->" + bindable + u"().setBinding(" + createBindingForBindable + u");";
+ *block += epilogue;
+ } else {
+ QString createBindingForNonBindable =
+ u" "_s
+ + u"QT_PREPEND_NAMESPACE(QQmlCppBinding)::createBindingForNonBindable(" + unitVarName
+ + u", " + scope + u", " + QString::number(functionIndex) + u", " + target + u", "
+ + QString::number(propertyIndex) + u", " + QString::number(valueTypeIndex) + u", "
+ + propName + u")";
+ // Note: in this version, the binding is set implicitly
+ *block << u"if (!initializedCache.contains(\"%1\"))"_s.arg(p.propertyName());
+ *block << createBindingForNonBindable + u";";
+ }
+}
+
+void QmltcCodeGenerator::generate_createTranslationBindingOnProperty(
+ QStringList *block, const TranslationBindingInfo &info)
+{
+ const QString propName = QQmlJSUtils::toLiteral(info.property.propertyName());
+ const QString qqmlTranslation = info.data.serializeForQmltc();
+
+ if (QString bindable = info.property.bindable(); !bindable.isEmpty()) {
+ // TODO: test that private properties are bindable
+ QString createTranslationCode = uR"(QT_PREPEND_NAMESPACE(QQmlCppBinding)
+ ::createTranslationBindingForBindable(%1, %2, %3, %4, %5))"_s
+ .arg(info.unitVarName, info.target)
+ .arg(info.propertyIndex)
+ .arg(qqmlTranslation, propName);
+
+ *block << QmltcCodeGenerator::wrap_privateClass(info.target, info.property) + u"->"
+ + bindable + u"().setBinding(" + createTranslationCode + u");";
+ } else {
+ QString locationString =
+ u"QQmlSourceLocation(%1->fileName(), %2, %3)"_s.arg(info.unitVarName)
+ .arg(info.line)
+ .arg(info.column);
+ QString createTranslationCode = uR"(QT_PREPEND_NAMESPACE(QQmlCppBinding)
+ ::createTranslationBindingForNonBindable(
+ %1, //unit
+ %2, //location
+ %3, //translationData
+ %4, //thisObject
+ %5, //bindingTarget
+ %6, //metaPropertyIndex
+ %7, //propertyName
+ %8) //valueTypePropertyIndex
+ )"_s.arg(info.unitVarName, locationString, qqmlTranslation, info.scope, info.target)
+ .arg(info.propertyIndex)
+ .arg(propName)
+ .arg(info.valueTypeIndex);
+ // Note: in this version, the binding is set implicitly
+ *block << createTranslationCode + u";";
+ }
+}
+
+QmltcCodeGenerator::PreparedValue
+QmltcCodeGenerator::wrap_mismatchingTypeConversion(const QQmlJSMetaProperty &p, QString value)
+{
+ auto isDerivedFromBuiltin = [](const QQmlJSScope::ConstPtr &derived, const QString &builtin) {
+ for (QQmlJSScope::ConstPtr t = derived; t; t = t->baseType()) {
+ if (t->internalName() == builtin)
+ return true;
+ }
+ return false;
+ };
+ QStringList prologue;
+ QStringList epilogue;
+ const QQmlJSScope::ConstPtr propType = p.type();
+ if (isDerivedFromBuiltin(propType, u"QVariant"_s)) {
+ const QString variantName = u"var_" + p.propertyName();
+ prologue << u"{ // accepts QVariant"_s;
+ prologue << u"QVariant " + variantName + u";";
+ prologue << variantName + u".setValue(" + value + u");";
+ epilogue << u"}"_s;
+ value = u"std::move(" + variantName + u")";
+ } else if (isDerivedFromBuiltin(propType, u"QJSValue"_s)) {
+ const QString jsvalueName = u"jsvalue_" + p.propertyName();
+ prologue << u"{ // accepts QJSValue"_s;
+ // Note: do not assume we have the engine, acquire it from `this`
+ prologue << u"auto e = qmlEngine(this);"_s;
+ prologue << u"QJSValue " + jsvalueName + u" = e->toScriptValue(" + value + u");";
+ epilogue << u"}"_s;
+ value = u"std::move(" + jsvalueName + u")";
+ }
+ return { prologue, value, epilogue };
+}
+
+QString QmltcCodeGenerator::wrap_privateClass(const QString &accessor, const QQmlJSMetaProperty &p)
+{
+ if (!p.isPrivate())
+ return accessor;
+
+ const QString privateType = p.privateClass();
+ return u"static_cast<" + privateType + u" *>(QObjectPrivate::get(" + accessor + u"))";
+}
+
+QString QmltcCodeGenerator::wrap_qOverload(const QList<QmltcVariable> &parameters,
+ const QString &overloaded)
+{
+ QStringList types;
+ types.reserve(parameters.size());
+ for (const QmltcVariable &p : parameters)
+ types.emplaceBack(p.cppType);
+ return u"qOverload<" + types.join(u", "_s) + u">(" + overloaded + u")";
+}
+
+QString QmltcCodeGenerator::wrap_addressof(const QString &addressed)
+{
+ return u"std::addressof(" + addressed + u")";
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qmltc/qmltccompilerpieces.h b/tools/qmltc/qmltccompilerpieces.h
new file mode 100644
index 0000000000..3252f19e86
--- /dev/null
+++ b/tools/qmltc/qmltccompilerpieces.h
@@ -0,0 +1,714 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef QMLTCCOMPILERPIECES_H
+#define QMLTCCOMPILERPIECES_H
+
+#include <QtCore/qscopeguard.h>
+#include <QtCore/qstringbuilder.h>
+#include <QtCore/qfileinfo.h>
+
+#include <private/qqmljsutils_p.h>
+#include <private/qqmlglobal_p.h>
+#include <private/qqmltranslation_p.h>
+
+#include "qmltcoutputir.h"
+#include "qmltcvisitor.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \internal
+
+ Helper class that generates code for the output IR. Takes care of
+ complicated, repetitive, nasty logic which is better kept in a single
+ confined place.
+*/
+struct QmltcCodeGenerator
+{
+ static const QString privateEngineName;
+ static const QString typeCountName;
+
+ QString documentUrl;
+ QmltcVisitor *visitor = nullptr;
+
+ using InlineComponentOrDocumentRootName = QQmlJSScope::InlineComponentOrDocumentRootName;
+ using RootDocumentNameType = QQmlJSScope::RootDocumentNameType;
+
+ [[nodiscard]] inline decltype(auto) generate_initCode(QmltcType &current,
+ const QQmlJSScope::ConstPtr &type) const;
+ inline void generate_initCodeForTopLevelComponent(QmltcType &current,
+ const QQmlJSScope::ConstPtr &type);
+
+ inline void generate_qmltcInstructionCallCode(QmltcMethod *function,
+ const QQmlJSScope::ConstPtr &type,
+ const QString &baseInstructionArgs,
+ const QString &childInstructionArgs) const;
+ inline void generate_endInitCode(QmltcType &current, const QQmlJSScope::ConstPtr &type) const;
+ inline void generate_setComplexBindingsCode(QmltcType &current,
+ const QQmlJSScope::ConstPtr &type) const;
+
+ inline void generate_interfaceCallCode(QmltcMethod *function, const QQmlJSScope::ConstPtr &type,
+ const QString &interfaceName,
+ const QString &interfaceCall) const;
+ inline void generate_beginClassCode(QmltcType &current,
+ const QQmlJSScope::ConstPtr &type) const;
+ inline void generate_completeComponentCode(QmltcType &current,
+ const QQmlJSScope::ConstPtr &type) const;
+ inline void generate_finalizeComponentCode(QmltcType &current,
+ const QQmlJSScope::ConstPtr &type) const;
+ inline void generate_handleOnCompletedCode(QmltcType &current,
+ const QQmlJSScope::ConstPtr &type) const;
+
+ static void generate_assignToProperty(QStringList *block, const QQmlJSScope::ConstPtr &type,
+ const QQmlJSMetaProperty &p, const QString &value,
+ const QString &accessor,
+ bool constructFromQObject = false);
+
+ static void generate_assignToListProperty(QStringList *block, const QQmlJSScope::ConstPtr &type,
+ const QQmlJSMetaProperty &p, const QStringList &value,
+ const QString &accessor, QString &qmlListVarName);
+
+ static void generate_setIdValue(QStringList *block, const QString &context, qsizetype index,
+ const QString &accessor, const QString &idString);
+
+ inline QString
+ generate_typeCount(const InlineComponentOrDocumentRootName &inlinedComponent) const
+ {
+ return generate_typeCount([](const QQmlJSScope::ConstPtr &) { return false; },
+ inlinedComponent);
+ }
+
+ /*!
+ * \internal
+ * Generate the constexpr typeCount expression for given inlinedComponent. Leave
+ * inlinedComponent empty to generate the expression for the main component.
+ */
+ template<typename Predicate>
+ inline QString
+ generate_typeCount(Predicate p,
+ const InlineComponentOrDocumentRootName &inlinedComponent) const;
+
+ static void generate_callExecuteRuntimeFunction(QStringList *block, const QString &url,
+ QQmlJSMetaMethod::AbsoluteFunctionIndex index,
+ const QString &accessor,
+ const QString &returnType,
+ const QList<QmltcVariable> &parameters = {});
+
+ static void generate_createBindingOnProperty(QStringList *block, const QString &unitVarName,
+ const QString &scope, qsizetype functionIndex,
+ const QString &target,
+ const QQmlJSScope::ConstPtr &targetType,
+ int propertyIndex, const QQmlJSMetaProperty &p,
+ int valueTypeIndex, const QString &subTarget);
+
+ // Used in generate_createTranslationBindingOnProperty to transport its numerous arguments.
+ struct TranslationBindingInfo
+ {
+ QString unitVarName;
+ QString scope;
+ QString target;
+ int propertyIndex;
+ QQmlJSMetaProperty property;
+
+ QQmlTranslation data;
+
+ int valueTypeIndex;
+ // For the source location of the translation binding
+ uint line;
+ // For the source location of the translation binding
+ uint column;
+ };
+
+ static void generate_createTranslationBindingOnProperty(QStringList *block,
+ const TranslationBindingInfo &info);
+
+ static inline void generate_getCompilationUnitFromUrl();
+
+ struct PreparedValue
+ {
+ QStringList prologue;
+ QString value;
+ QStringList epilogue;
+ };
+
+ static PreparedValue wrap_mismatchingTypeConversion(const QQmlJSMetaProperty &p, QString value);
+ static PreparedValue wrap_extensionType(const QQmlJSScope::ConstPtr &type,
+ const QQmlJSMetaProperty &p, const QString &accessor);
+
+ static QString wrap_privateClass(const QString &accessor, const QQmlJSMetaProperty &p);
+ static QString wrap_qOverload(const QList<QmltcVariable> &parameters,
+ const QString &overloaded);
+ static QString wrap_addressof(const QString &addressed);
+
+ QString urlMethodName() const
+ {
+ using namespace Qt::StringLiterals;
+ QFileInfo fi(documentUrl);
+ return u"q_qmltc_docUrl_" + fi.fileName().replace(u".qml"_s, u""_s).replace(u'.', u'_');
+ }
+};
+
+/*!
+ \internal
+
+ Generates \a{current.init}'s code. The init method sets up a
+ QQmlContext for the object and (in case \a type is a document
+ root) calls other object creation methods, and a user-provided
+ initialization callback, in a well-defined order:
+ 1. current.beginClass
+ 2. current.endInit
+ 3. user-provided initialization function
+ 4. current.setComplexBindings
+ 5. current.completeComponent
+ 6. current.finalizeComponent
+ 7. current.handleOnCompleted
+
+ This function returns a QScopeGuard with the final instructions that have to
+ be generated at a later point, once everything else is compiled.
+
+ \sa generate_initCodeForTopLevelComponent
+*/
+inline decltype(auto) QmltcCodeGenerator::generate_initCode(QmltcType &current,
+ const QQmlJSScope::ConstPtr &type) const
+{
+ using namespace Qt::StringLiterals;
+
+ // qmltc_init()'s parameters:
+ // * QQmltcObjectCreationHelper* creator
+ // * QQmlEngine* engine
+ // * const QQmlRefPointer<QQmlContextData>& parentContext
+ // * bool canFinalize [optional, when document root]
+ const bool isDocumentRoot = type == visitor->result();
+ const bool isInlineComponent = type->isInlineComponent();
+
+ current.init.body << u"Q_UNUSED(creator)"_s; // can happen sometimes
+
+ current.init.body << u"auto context = parentContext;"_s;
+
+ // if parent scope has a QML base type and is not a (current) document root,
+ // the parentContext we passed as input to this object is a context of
+ // another document. we need to fix it by using parentContext->parent()
+
+ const auto realQmlScope = [](const QQmlJSScope::ConstPtr &scope) {
+ if (scope->isArrayScope())
+ return scope->parentScope();
+ return scope;
+ };
+
+ if (auto parentScope = realQmlScope(type->parentScope());
+ parentScope != visitor->result() && QQmlJSUtils::hasCompositeBase(parentScope)) {
+ current.init.body << u"// NB: context->parent() is the context of this document"_s;
+ current.init.body << u"context = context->parent();"_s;
+ }
+
+ // any object with QML base class has to call base's init method
+ if (auto base = type->baseType(); base->isComposite()) {
+ QString lhs;
+ // init creates new context. for document root, it's going to be a real
+ // parent context, so store it temporarily in `context` variable
+ if (isDocumentRoot || isInlineComponent)
+ lhs = u"context = "_s;
+ current.init.body << u"// 0. call base's init method"_s;
+
+ const auto isCurrentType = [&](const QQmlJSScope::ConstPtr &qmlType) {
+ return qmlType == type;
+ };
+ const QString creationOffset =
+ generate_typeCount(isCurrentType, type->enclosingInlineComponentName());
+
+ current.init.body << u"{"_s;
+ current.init.body << u"QQmltcObjectCreationHelper subCreator(creator, %1);"_s.arg(
+ creationOffset);
+ current.init.body
+ << QStringLiteral("%1%2::%3(&subCreator, engine, context, /* finalize */ false);")
+ .arg(lhs, base->internalName(), current.init.name);
+ current.init.body << u"}"_s;
+ }
+
+ current.init.body
+ << QStringLiteral("auto %1 = QQmlEnginePrivate::get(engine);").arg(privateEngineName);
+ current.init.body << QStringLiteral("Q_UNUSED(%1)").arg(privateEngineName); // precaution
+
+ // when generating root or inlineComponents, we need to create a new (document-level) context.
+ // otherwise, just use existing context as is
+ if (isDocumentRoot || isInlineComponent) {
+ current.init.body << u"// 1. create new QML context for this document"_s;
+ current.init.body
+ << QStringLiteral(
+ "context = %1->createInternalContext(%1->compilationUnitFromUrl(%2()), "
+ "context, %3, true);")
+ .arg(privateEngineName, urlMethodName())
+ .arg(this->visitor->creationIndex(type));
+ } else {
+ current.init.body << u"// 1. use current context as this object's context"_s;
+ current.init.body << u"// context = context;"_s;
+ }
+
+ if (!type->baseType()->isComposite() || isDocumentRoot || isInlineComponent) {
+ current.init.body << u"// 2. set context for this object"_s;
+ current.init.body << QStringLiteral(
+ "%1->setInternalContext(this, context, QQmlContextData::%2);")
+ .arg(privateEngineName,
+ (isDocumentRoot ? u"DocumentRoot"_s
+ : u"OrdinaryObject"_s));
+ if (isDocumentRoot || isInlineComponent)
+ current.init.body << u"context->setContextObject(this);"_s;
+ }
+
+ // context is this document's context. we must remember it in each type
+ current.variables.emplaceBack(u"QQmlRefPointer<QQmlContextData>"_s, u"q_qmltc_thisContext"_s,
+ u"nullptr"_s);
+ current.init.body << u"%1::q_qmltc_thisContext = context;"_s.arg(type->internalName());
+
+ if (int id = visitor->runtimeId(type); id >= 0) {
+ current.init.body << u"// 3. set id since it is provided"_s;
+ QString idString = visitor->addressableScopes().id(type, type);
+ if (idString.isEmpty())
+ idString = u"<unknown>"_s;
+ QmltcCodeGenerator::generate_setIdValue(&current.init.body, u"context"_s, id, u"this"_s,
+ idString);
+ }
+
+ // if type has an extension, create a dynamic meta object for it
+ bool hasExtension = false;
+ for (auto cppBase = QQmlJSScope::nonCompositeBaseType(type); cppBase;
+ cppBase = cppBase->baseType()) {
+ // QObject is special: we have a pseudo-extension on it due to builtins
+ if (cppBase->internalName() == u"QObject"_s)
+ break;
+ if (cppBase->extensionType().extensionSpecifier != QQmlJSScope::NotExtension) {
+ hasExtension = true;
+ break;
+ }
+ }
+ if (hasExtension) {
+ current.init.body << u"{"_s;
+ current.init.body << u"auto cppData = QmltcTypeData(this);"_s;
+ current.init.body << u"qmltcCreateDynamicMetaObject(this, cppData);"_s;
+ current.init.body << u"}"_s;
+ }
+
+ const auto generateFinalLines = [&current, isDocumentRoot, isInlineComponent]() {
+ if (isDocumentRoot || isInlineComponent) {
+ current.init.body << u"// 4. finish the document root creation"_s;
+ current.init.body << u"if (canFinalize) {"_s;
+ current.init.body << QStringLiteral(" %1(creator, /* finalize */ true);")
+ .arg(current.beginClass.name);
+ current.init.body << QStringLiteral(" %1(creator, engine);")
+ .arg(current.endInit.name);
+
+ current.init.body << QStringLiteral(" {");
+ current.init.body << QStringLiteral(" PropertyInitializer propertyInitializer(*this);");
+ current.init.body << QStringLiteral(" initializer(propertyInitializer);");
+ current.init.body << QStringLiteral(" %1(creator, engine, propertyInitializer.initializedCache);").arg(current.setComplexBindings.name);
+ current.init.body << QStringLiteral(" }");
+
+
+ current.init.body << QStringLiteral(" %1(creator, /* finalize */ true);")
+ .arg(current.completeComponent.name);
+ current.init.body << QStringLiteral(" %1(creator, /* finalize */ true);")
+ .arg(current.finalizeComponent.name);
+ current.init.body << QStringLiteral(" %1(creator);")
+ .arg(current.handleOnCompleted.name);
+ current.init.body << u"}"_s;
+ }
+ current.init.body << u"return context;"_s;
+ };
+
+ return QScopeGuard(generateFinalLines);
+}
+
+/*!
+ \internal
+
+ Generates \a{current.init}'s code in case when \a type is a top-level
+ Component type. The init method in this case mimics
+ QQmlObjectCreator::createComponent() logic.
+
+ \sa generate_initCode
+*/
+inline void
+QmltcCodeGenerator::generate_initCodeForTopLevelComponent(QmltcType &current,
+ const QQmlJSScope::ConstPtr &type)
+{
+ Q_UNUSED(type);
+
+ using namespace Qt::StringLiterals;
+
+ // since we create a document root as QQmlComponent, we only need to fake
+ // QQmlComponent construction in init:
+ current.init.body << u"// init QQmlComponent: see QQmlObjectCreator::createComponent()"_s;
+ current.init.body << u"{"_s;
+ // we already called QQmlComponent(parent) constructor. now we need:
+ // 1. QQmlComponent(engine, parent) logic:
+ current.init.body << u"// QQmlComponent(engine, parent):"_s;
+ current.init.body << u"auto d = QQmlComponentPrivate::get(this);"_s;
+ current.init.body << u"Q_ASSERT(d);"_s;
+ current.init.body << u"d->engine = engine;"_s;
+ current.init.body << u"QObject::connect(engine, &QObject::destroyed, this, [d]() {"_s;
+ current.init.body << u" d->state.creator.reset();"_s;
+ current.init.body << u" d->engine = nullptr;"_s;
+ current.init.body << u"});"_s;
+ // 2. QQmlComponent(engine, compilationUnit, start, parent) logic:
+ current.init.body << u"// QQmlComponent(engine, compilationUnit, start, parent):"_s;
+ current.init.body
+ << u"auto compilationUnit = QQmlEnginePrivate::get(engine)->compilationUnitFromUrl("
+ + QmltcCodeGenerator::urlMethodName() + u"());";
+ current.init.body << u"d->compilationUnit = compilationUnit;"_s;
+ current.init.body << u"d->start = 0;"_s;
+ current.init.body << u"d->url = compilationUnit->finalUrl();"_s;
+ current.init.body << u"d->progress = 1.0;"_s;
+ // 3. QQmlObjectCreator::createComponent() logic which is left:
+ current.init.body << u"// QQmlObjectCreator::createComponent():"_s;
+ current.init.body << u"d->creationContext = context;"_s;
+ current.init.body << u"Q_ASSERT(QQmlData::get(this, /*create*/ false));"_s;
+ current.init.body << u"}"_s;
+}
+
+/*!
+ \internal
+
+ A generic helper function that generates special qmltc instruction code
+ boilerplate, adding it to a passed \a function. This is a building block
+ used to generate e.g. QML_endInit code.
+*/
+inline void QmltcCodeGenerator::generate_qmltcInstructionCallCode(
+ QmltcMethod *function, const QQmlJSScope::ConstPtr &type,
+ const QString &baseInstructionArgs, const QString &childInstructionArgs) const
+{
+ using namespace Qt::StringLiterals;
+
+ if (auto base = type->baseType(); base->isComposite()) {
+ function->body << u"// call base's method"_s;
+ const auto isCurrentType = [&](const QQmlJSScope::ConstPtr &qmlType) {
+ return qmlType == type;
+ };
+ const QString creationOffset =
+ generate_typeCount(isCurrentType, type->enclosingInlineComponentName());
+ function->body << u"{"_s;
+ function->body << u"QQmltcObjectCreationHelper subCreator(creator, %1);"_s.arg(
+ creationOffset);
+ if (!baseInstructionArgs.isEmpty()) {
+ function->body << u"%1::%2(&subCreator, %3);"_s.arg(
+ base->internalName(), function->name, baseInstructionArgs);
+ } else {
+ function->body << u"%1::%2(&subCreator);"_s.arg(base->internalName(), function->name);
+ }
+ function->body << u"}"_s;
+ }
+
+ const bool isDocumentRoot = type == visitor->result();
+ const bool isInlineComponent = type->isInlineComponent();
+
+ if (!(isDocumentRoot
+ || isInlineComponent)) // document/inline component root does all the work here
+ return;
+ auto name = isInlineComponent
+ ? InlineComponentOrDocumentRootName(*type->inlineComponentName())
+ : InlineComponentOrDocumentRootName(QQmlJSScope::RootDocumentNameType());
+ const auto types = visitor->pureQmlTypes(name);
+ function->body << u"// call children's methods"_s;
+ for (qsizetype i = 1; i < types.size(); ++i) {
+ const auto &type = types[i];
+ Q_ASSERT(!type->isComponentRootElement());
+ function->body << u"creator->get<%1>(%2)->%3(%4);"_s.arg(
+ type->internalName(), QString::number(i), function->name, childInstructionArgs);
+ }
+ function->body << u"// call own method code"_s;
+}
+
+/*!
+ \internal
+
+ Generates \a{current.endInit}'s code. The endInit method creates bindings,
+ connects signals with slots and generally performs other within-object
+ initialization. Additionally, the QML document root's endInit calls endInit
+ methods of all the necessary QML types within the document.
+*/
+inline void QmltcCodeGenerator::generate_endInitCode(QmltcType &current,
+ const QQmlJSScope::ConstPtr &type) const
+{
+ using namespace Qt::StringLiterals;
+
+ // QML_endInit()'s parameters:
+ // * QQmltcObjectCreationHelper* creator
+ // * QQmlEngine* engine
+ current.endInit.body << u"Q_UNUSED(creator)"_s;
+ current.endInit.body << u"Q_UNUSED(engine)"_s;
+
+ generate_qmltcInstructionCallCode(&current.endInit, type, u"engine"_s, u"creator, engine"_s);
+
+ if (visitor->hasDeferredBindings(type)) {
+ current.endInit.body << u"{ // defer bindings"_s;
+ current.endInit.body << u"auto ddata = QQmlData::get(this);"_s;
+ current.endInit.body << u"auto thisContext = ddata->outerContext;"_s;
+ current.endInit.body << u"Q_ASSERT(thisContext);"_s;
+ current.endInit.body << QStringLiteral("ddata->deferData(%1, "
+ "QQmlEnginePrivate::get(engine)->"
+ "compilationUnitFromUrl(%2()), thisContext);")
+ .arg(QString::number(visitor->qmlIrObjectIndex(type)),
+ QmltcCodeGenerator::urlMethodName());
+ current.endInit.body << u"}"_s;
+ }
+}
+
+/*!
+ \internal
+
+ Generates \a{current.setComplexBindings}'s code. The setComplexBindings
+ method creates complex bindings (such as script bindings). Additionally, the
+ QML document root's setComplexBindings calls setComplexBindings methods of
+ all the necessary QML types within the document.
+*/
+inline void
+QmltcCodeGenerator::generate_setComplexBindingsCode(QmltcType &current,
+ const QQmlJSScope::ConstPtr &type) const
+{
+ using namespace Qt::StringLiterals;
+
+ // QML_setComplexBindings()'s parameters:
+ // * QQmltcObjectCreationHelper* creator
+ // * QQmlEngine* engine
+ current.setComplexBindings.body << u"Q_UNUSED(creator)"_s;
+ current.setComplexBindings.body << u"Q_UNUSED(engine)"_s;
+
+ generate_qmltcInstructionCallCode(&current.setComplexBindings, type, u"engine"_s,
+ u"creator, engine"_s);
+}
+
+/*!
+ \internal
+
+ A generic helper function that generates interface code boilerplate, adding
+ it to a passed \a function. This is a building block used to generate e.g.
+ QQmlParserStatus API calls.
+*/
+inline void QmltcCodeGenerator::generate_interfaceCallCode(QmltcMethod *function,
+ const QQmlJSScope::ConstPtr &type,
+ const QString &interfaceName,
+ const QString &interfaceCall) const
+{
+ using namespace Qt::StringLiterals;
+
+ // function's parameters:
+ // * QQmltcObjectCreationHelper* creator
+ // * bool canFinalize [optional, when document root or inline component root]
+ const bool isDocumentRoot = type == visitor->result();
+ const bool isInlineComponent = type->isInlineComponent();
+ function->body << u"Q_UNUSED(creator)"_s;
+ if (isDocumentRoot || isInlineComponent)
+ function->body << u"Q_UNUSED(canFinalize)"_s;
+
+ if (auto base = type->baseType(); base->isComposite()) {
+ function->body << u"// call base's method"_s;
+ const auto isCurrentType = [&](const QQmlJSScope::ConstPtr &qmlType) {
+ return qmlType == type;
+ };
+ const QString creationOffset =
+ generate_typeCount(isCurrentType, type->enclosingInlineComponentName());
+ function->body << u"{"_s;
+ function->body << u"QQmltcObjectCreationHelper subCreator(creator, %1);"_s.arg(
+ creationOffset);
+ function->body << u"%1::%2(&subCreator, /* finalize */ false);"_s.arg(base->internalName(),
+ function->name);
+ function->body << u"}"_s;
+ }
+
+ if (!(isDocumentRoot || isInlineComponent))
+ return;
+
+ auto name = isInlineComponent
+ ? InlineComponentOrDocumentRootName(*type->inlineComponentName())
+ : InlineComponentOrDocumentRootName(QQmlJSScope::RootDocumentNameType());
+
+ const auto types = visitor->pureQmlTypes(name);
+ function->body << u"// call children's methods"_s;
+ for (qsizetype i = 1; i < types.size(); ++i) {
+ const auto &type = types[i];
+ Q_ASSERT(!type->isComponentRootElement());
+ function->body << u"{"_s;
+ function->body << u"auto child = creator->get<%1>(%2);"_s.arg(type->internalName(),
+ QString::number(i));
+ function->body << u"child->%1(creator);"_s.arg(function->name);
+ if (type->hasInterface(interfaceName)) {
+ function->body << u"static_assert(std::is_base_of<%1, %2>::value);"_s.arg(
+ interfaceName, type->internalName());
+ function->body << u"child->%1();"_s.arg(interfaceCall);
+ }
+ function->body << u"}"_s;
+ }
+
+ if (type->hasInterface(interfaceName)) {
+ function->body << u"if (canFinalize) {"_s;
+ function->body << u" // call own method"_s;
+ function->body << u" static_assert(std::is_base_of<%1, %2>::value);"_s.arg(
+ interfaceName, type->internalName());
+ function->body << u" this->%1();"_s.arg(interfaceCall);
+ function->body << u"}"_s;
+ }
+}
+
+/*!
+ \internal
+
+ Generates \a{current.beginClass}'s code. The beginClass method optionally
+ calls QQmlParserStatus::classBegin() when \a type implements the
+ corresponding interface.
+*/
+inline void QmltcCodeGenerator::generate_beginClassCode(QmltcType &current,
+ const QQmlJSScope::ConstPtr &type) const
+{
+ using namespace Qt::StringLiterals;
+ generate_interfaceCallCode(&current.beginClass, type, u"QQmlParserStatus"_s, u"classBegin"_s);
+}
+
+/*!
+ \internal
+
+ Generates \a{current.completeComponent}'s code. The completeComponent method
+ optionally calls QQmlParserStatus::componentComplete() when \a type
+ implements the corresponding interface.
+*/
+inline void
+QmltcCodeGenerator::generate_completeComponentCode(QmltcType &current,
+ const QQmlJSScope::ConstPtr &type) const
+{
+ using namespace Qt::StringLiterals;
+ generate_interfaceCallCode(&current.completeComponent, type, u"QQmlParserStatus"_s,
+ u"componentComplete"_s);
+}
+
+/*!
+ \internal
+
+ Generates \a{current.finalizeComponent}'s code. The finalizeComponent method
+ optionally calls QQmlFinalizerHook::componentFinalized() when \a type
+ implements the corresponding interface.
+*/
+inline void
+QmltcCodeGenerator::generate_finalizeComponentCode(QmltcType &current,
+ const QQmlJSScope::ConstPtr &type) const
+{
+ using namespace Qt::StringLiterals;
+ generate_interfaceCallCode(&current.finalizeComponent, type, u"QQmlFinalizerHook"_s,
+ u"componentFinalized"_s);
+}
+
+/*!
+ \internal
+
+ Generates \a{current.handleOnCompleted}'s code. The handleOnCompleted method
+ optionally calls a Component.onCompleted handler if that is present in \a
+ type.
+*/
+inline void
+QmltcCodeGenerator::generate_handleOnCompletedCode(QmltcType &current,
+ const QQmlJSScope::ConstPtr &type) const
+{
+ using namespace Qt::StringLiterals;
+
+ // QML_handleOnCompleted()'s parameters:
+ // * QQmltcObjectCreationHelper* creator
+ current.handleOnCompleted.body << u"Q_UNUSED(creator)"_s;
+
+ generate_qmltcInstructionCallCode(&current.handleOnCompleted, type, QString(), u"creator"_s);
+}
+
+/*!
+ \internal
+
+ Generates a constexpr function consisting of a sum of type counts for a
+ current QML document. Predicate \a p acts as a stop condition to prematurely
+ end the sum generation.
+
+ The high-level idea:
+
+ Each qmltc-compiled document root has a notion of type count. Type count is
+ a number of types the current QML document contains (except for
+ Component-wrapped types) plus the sum of all type counts of all the QML
+ documents used in the current document: if current document has a type with
+ QML base type, this type's type count is added to the type count of the
+ current document.
+
+ To be able to lookup created objects during the creation process, one needs
+ to know an index of each object within the document + an offset of the
+ document. Index comes from QmltcVisitor and is basically a serial number of
+ a type in the document (index < type count of the document root type). The
+ offset is more indirect.
+
+ The current document always starts with an offset of 0, each type that has a
+ QML base type also "has a sub-document". Each sub-document has a non-0
+ offset X, where X is calculated as a sum of the current document's type
+ count and a cumulative type count of all the previous sub-documents that
+ appear before the sub-document of interest:
+
+ \code
+ // A.qml
+ Item { // offset: 0; number of types == 1 (document root) + 3 (children)
+
+ QmlBase1 { } // offset: 4 (number of types in A.qml itself)
+
+ QmlBase2 { } // offset: 4 + N, where N == typeCount(QmlBase1.qml)
+
+ QmlBase3 { } // offset: (4 + N) + M, where M == typeCount(QmlBase2.qml)
+
+ } // typeCount(A.qml) == 4 + N + M + O, where O == typeCount(QmlBase3.qml)
+ \endcode
+
+ As all objects are put into an array, schematically you can look at it in
+ the following way:
+
+ ```
+ count: 4 N M O
+ objects: aaaa|xxxxxxxxxxxxx|yyyyyyy|zzz
+ ^ ^ ^ ^
+ files: | QmlBase1.qml | QmlBase3.qml
+ A.qml QmlBase2.qml
+ ```
+
+ For the object lookup logic itself, see QQmltcObjectCreationHelper
+*/
+template<typename Predicate>
+inline QString QmltcCodeGenerator::generate_typeCount(
+ Predicate p, const InlineComponentOrDocumentRootName &inlinedComponent) const
+{
+ using namespace Qt::StringLiterals;
+
+ const QList<QQmlJSScope::ConstPtr> typesWithBaseTypeCount =
+ visitor->qmlTypesWithQmlBases(inlinedComponent);
+ QStringList components;
+ components.reserve(1 + typesWithBaseTypeCount.size());
+
+ Q_ASSERT(visitor->pureQmlTypes(inlinedComponent).size() > 0);
+ Q_ASSERT(visitor->typeCount(inlinedComponent)
+ >= visitor->pureQmlTypes(inlinedComponent).size());
+ qsizetype typeCount = visitor->typeCount(inlinedComponent);
+
+ // add this document's type counts minus document root (if not an inline component)
+ if (std::holds_alternative<RootDocumentNameType>(inlinedComponent))
+ typeCount--;
+ components << QString::number(typeCount);
+
+ // traverse types with QML base classes
+ for (const QQmlJSScope::ConstPtr &t : typesWithBaseTypeCount) {
+ if (p(t))
+ break;
+ QString typeCountTemplate = u"QQmltcObjectCreationHelper::typeCount<%1>()"_s;
+ if (t == visitor->result()) { // t is this document's root
+ components << typeCountTemplate.arg(t->baseTypeName());
+ } else if (t->isInlineComponent()) {
+ // inline components always have a base class, by definition
+ Q_ASSERT(t->baseType());
+ components << typeCountTemplate.arg(t->baseType()->internalName());
+ } else {
+ components << typeCountTemplate.arg(t->internalName());
+ }
+ }
+
+ return components.join(u" + "_s);
+}
+
+QT_END_NAMESPACE
+
+#endif // QMLTCCOMPILERPIECES_H
diff --git a/tools/qmltc/qmltcoutputir.h b/tools/qmltc/qmltcoutputir.h
new file mode 100644
index 0000000000..de531f718d
--- /dev/null
+++ b/tools/qmltc/qmltcoutputir.h
@@ -0,0 +1,195 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef QMLTCOUTPUTIR_H
+#define QMLTCOUTPUTIR_H
+
+#include <QtCore/qstring.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/qset.h>
+
+#include <private/qqmljsmetatypes_p.h>
+
+#include <optional>
+
+QT_BEGIN_NAMESPACE
+
+// Below are the classes that represent compiled QML types in a string data
+// form. These classes are used to generate C++ code.
+
+// Represents C++ variable
+struct QmltcVariable
+{
+ QString cppType; // C++ type of a variable
+ QString name; // variable name
+ QString defaultValue; // optional initialization value
+
+ QmltcVariable() = default;
+ // special ctor for QList's emplace back
+ QmltcVariable(const QString &t, const QString &n, const QString &v = QString())
+ : cppType(t), name(n), defaultValue(v)
+ {
+ }
+};
+
+struct QmltcProperty : QmltcVariable
+{
+ QString containingClass;
+ QString signalName;
+
+ QmltcProperty() = default;
+ QmltcProperty(const QString t, const QString &n, const QString &c, const QString &s)
+ : QmltcVariable(t, n), containingClass(c), signalName(s)
+ {
+ }
+};
+
+// Represents QML -> C++ compiled enumeration type
+struct QmltcEnum
+{
+ QString cppType; // C++ type of an enum
+ QStringList keys; // enumerator keys
+ QStringList values; // enumerator values
+ QString ownMocLine; // special MOC line that follows enum declaration
+
+ QmltcEnum() = default;
+ QmltcEnum(const QString &t, const QStringList &ks, const QStringList &vs, const QString &l)
+ : cppType(t), keys(ks), values(vs), ownMocLine(l)
+ {
+ }
+};
+
+struct QmltcMethodBase
+{
+ QStringList comments; // C++ comments
+ QString name; // C++ function name
+ QList<QmltcVariable> parameterList; // C++ function parameter list
+ QStringList body; // C++ function code
+ QQmlJSMetaMethod::Access access = QQmlJSMetaMethod::Public; // access specifier
+ QStringList declarationPrefixes;
+ QStringList modifiers; // cv-qualifiers, ref-qualifier, noexcept, attributes
+};
+
+// Represents QML -> C++ compiled function
+struct QmltcMethod : QmltcMethodBase
+{
+ QString returnType; // C++ return type
+ QQmlJSMetaMethodType type = QQmlJSMetaMethodType::Method; // Qt function type
+
+ // TODO: should be a better way to handle this
+ bool userVisible = false; // tells if a function is prioritized during the output generation
+};
+
+// Represents C++ ctor of a type
+struct QmltcCtor : QmltcMethodBase
+{
+ QStringList initializerList; // C++ ctor's initializer list
+};
+
+// Represents C++ dtor of a type
+struct QmltcDtor : QmltcMethodBase
+{
+};
+
+// Represents a generated class that knows how to set the public,
+// writable properties of a compiled QML -> C++ type.
+// This is generally intended to be available for the root of the
+// document to allow the user to set the initial values for
+// properties, when creating a component, with support for strong
+// typing.
+struct QmltcPropertyInitializer {
+ QString name;
+
+ QmltcCtor constructor;
+
+ // A member containing a reference to the object for which the
+ // properties should be set.
+ QmltcVariable component;
+
+ // A member containing a cache of properties that were actually
+ // set that can be referenced later..
+ QmltcVariable initializedCache;
+
+ // Setter methods for each property.
+ QList<QmltcMethod> propertySetters;
+};
+
+// Represents a generated class that contains a bundle of values to
+// initialize the required properties of a type.
+//
+// This is generally intended to be available for the root component
+// of the document, where it will be used as a constructor argument to
+// force the user to provide initial values for the required
+// properties of the constructed type.
+struct QmltcRequiredPropertiesBundle {
+ QString name;
+
+ QList<QmltcVariable> members;
+};
+
+// Represents QML -> C++ compiled type
+struct QmltcType
+{
+ QString cppType; // C++ type of the QML type
+ QStringList baseClasses; // C++ type names of base classes
+ QStringList mocCode; // Qt MOC code
+ QStringList otherCode; // Random code that doesn't fit any category, e.g. friend declarations
+
+ // member types: enumerations and child types
+ QList<QmltcEnum> enums;
+ QList<QmltcType> children; // these are pretty much always empty
+
+ // special member functions:
+ QmltcCtor baselineCtor {}; // does basic contruction
+ QmltcCtor externalCtor {}; // calls basicCtor, calls init
+ QmltcMethod init {}; // starts object initialization (context setup), calls finalize
+ QmltcMethod beginClass {}; // calls QQmlParserStatus::classBegin()
+ QmltcMethod endInit {}; // ends object initialization (with "simple" bindings setup)
+ QmltcMethod setComplexBindings {}; // sets up "complex" (e.g. script) bindings
+ QmltcMethod completeComponent {}; // calls QQmlParserStatus::componentComplete()
+ QmltcMethod finalizeComponent {}; // calls QQmlFinalizerHook::componentFinalized()
+ QmltcMethod handleOnCompleted {}; // calls Component.onCompleted
+
+ std::optional<QmltcDtor> dtor {};
+
+ // member functions: methods, signals and slots
+ QList<QmltcMethod> functions;
+ // member variables
+ QList<QmltcVariable> variables;
+ QList<QmltcProperty> properties;
+
+ // QML document root specific:
+ std::optional<QmltcMethod> typeCount; // the number of QML types defined in a document
+
+ // TODO: only needed for binding callables - should not be needed, generally
+ bool ignoreInit = false; // specifies whether init and externalCtor should be ignored
+
+ // needed for singletons
+ std::optional<QmltcMethod> staticCreate{};
+
+ // A proxy class that provides a restricted interface that only
+ // allows setting the properties of the type.
+ QmltcPropertyInitializer propertyInitializer{};
+
+ std::optional<QmltcRequiredPropertiesBundle> requiredPropertiesBundle{};
+};
+
+// Represents whole QML program, compiled to C++
+struct QmltcProgram
+{
+ QString url; // QML file url
+ QString cppPath; // C++ output .cpp path
+ QString hPath; // C++ output .h path
+ QString outNamespace;
+ QString exportMacro; // if not empty, the macro that should be used to export the generated
+ // classes
+ QSet<QString> includes; // non-default C++ include files
+ QmltcMethod urlMethod; // returns QUrl of the QML document
+
+ QList<QmltcType> compiledTypes; // all QML types that are compiled to C++
+};
+
+QT_END_NAMESPACE
+
+#endif // QMLTCOUTPUTIR_H
diff --git a/tools/qmltc/qmltcoutputprimitives.h b/tools/qmltc/qmltcoutputprimitives.h
new file mode 100644
index 0000000000..dae523b0d7
--- /dev/null
+++ b/tools/qmltc/qmltcoutputprimitives.h
@@ -0,0 +1,100 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef QMLTCOUTPUTPRIMITIVES_H
+#define QMLTCOUTPUTPRIMITIVES_H
+
+#include <QtCore/qstack.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qstringbuilder.h>
+
+QT_BEGIN_NAMESPACE
+
+struct QmltcOutput
+{
+ QString header;
+ QString cpp;
+};
+
+class QmltcOutputWrapper
+{
+ QmltcOutput &m_code;
+
+ template<typename String>
+ static void rawAppend(QString &out, const String &what, int extraIndent = 0)
+ {
+ constexpr char16_t newLine[] = u"\n";
+ out += QString(extraIndent * 4, u' ') + what + newLine;
+ }
+
+public:
+ QmltcOutputWrapper(QmltcOutput &code) : m_code(code) { }
+ const QmltcOutput &code() const { return m_code; }
+
+ QStack<QString> memberScopes; // member name scopes e.g. MyClass::MySubclass::
+ int headerIndent = 0; // header indentation level
+ int cppIndent = 0; // cpp indentation level
+
+ // manages current scope of the generated code, which is necessary for
+ // cpp file generation. Example:
+ // class MyClass { MyClass(); }; - in header
+ // MyClass::MyClass() {} - in cpp
+ // MemberNameScope makes sure "MyClass::" is recorded
+ struct MemberNameScope
+ {
+ QmltcOutputWrapper *m_code;
+ MemberNameScope(QmltcOutputWrapper *code, const QString &str) : m_code(code)
+ {
+ m_code->memberScopes.push(str);
+ }
+ ~MemberNameScope() { m_code->memberScopes.pop(); }
+ Q_DISABLE_COPY_MOVE(MemberNameScope)
+ };
+
+ struct HeaderIndentationScope
+ {
+ QmltcOutputWrapper *m_code;
+ HeaderIndentationScope(QmltcOutputWrapper *code) : m_code(code) { ++m_code->headerIndent; }
+ ~HeaderIndentationScope() { --m_code->headerIndent; }
+ Q_DISABLE_COPY_MOVE(HeaderIndentationScope)
+ };
+
+ struct CppIndentationScope
+ {
+ QmltcOutputWrapper *m_code;
+ CppIndentationScope(QmltcOutputWrapper *code) : m_code(code) { ++m_code->cppIndent; }
+ ~CppIndentationScope() { --m_code->cppIndent; }
+ Q_DISABLE_COPY_MOVE(CppIndentationScope)
+ };
+
+ // appends string \a what with extra indentation \a extraIndent to current
+ // header string
+ template<typename String>
+ void rawAppendToHeader(const String &what, int extraIndent = 0)
+ {
+ rawAppend(m_code.header, what, headerIndent + extraIndent);
+ }
+
+ // appends string \a what with extra indentation \a extraIndent to current
+ // cpp string
+ template<typename String>
+ void rawAppendToCpp(const String &what, int extraIndent = 0)
+ {
+ rawAppend(m_code.cpp, what, cppIndent + extraIndent);
+ }
+
+ // special case of rawAppendToCpp that makes sure that string "foo()"
+ // becomes "MyClass::foo()"
+ template<typename String>
+ void rawAppendSignatureToCpp(const String &what, int extraIndent = 0)
+ {
+ QString signatureScope;
+ for (const auto &scope : memberScopes)
+ signatureScope += scope + u"::";
+ rawAppendToCpp(signatureScope + what, extraIndent);
+ }
+};
+
+QT_END_NAMESPACE
+
+#endif // QMLTCOUTPUTPRIMITIVES_H
diff --git a/tools/qmltc/qmltcpropertyutils.h b/tools/qmltc/qmltcpropertyutils.h
new file mode 100644
index 0000000000..8a69e5ef09
--- /dev/null
+++ b/tools/qmltc/qmltcpropertyutils.h
@@ -0,0 +1,55 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef QMLTCPROPERTYUTILS_H
+#define QMLTCPROPERTYUTILS_H
+
+#include <private/qqmljsmetatypes_p.h>
+#include <private/qqmljsscope_p.h>
+#include <QtQml/private/qqmlsignalnames_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \internal
+
+ Returns an underlying C++ type of \a p property.
+*/
+inline QString getUnderlyingType(const QQmlJSMetaProperty &p)
+{
+ if (p.isList()) {
+ // We cannot just use p.type()->internalName() here because it may be
+ // a list property of something that only receives a C++ name from qmltc.
+ const QQmlJSScope::ConstPtr valueType = p.type()->valueType();
+ return (valueType->isReferenceType() ? u"QQmlListProperty<" : u"QList<")
+ + valueType->internalName() + u'>';
+ }
+
+ return p.type()->augmentedInternalName();
+}
+
+// simple class that, for a given property, creates information for the
+// Q_PROPERTY macro (READ/WRITE function names, etc.)
+struct QmltcPropertyData
+{
+ QmltcPropertyData(const QQmlJSMetaProperty &p) : QmltcPropertyData(p.propertyName()) { }
+
+ QmltcPropertyData(const QString &propertyName)
+ {
+ read = propertyName;
+ write = QQmlSignalNames::addPrefixToPropertyName(u"set", propertyName);
+ bindable = QQmlSignalNames::addPrefixToPropertyName(u"bindable", propertyName);
+ notify = QQmlSignalNames::propertyNameToChangedSignalName(propertyName);
+ reset = QQmlSignalNames::addPrefixToPropertyName(u"reset", propertyName);
+ }
+
+ QString read;
+ QString write;
+ QString bindable;
+ QString notify;
+ QString reset;
+};
+
+QT_END_NAMESPACE
+
+#endif // QMLTCPROPERTYUTILS_H
diff --git a/tools/qmltc/qmltctyperesolver.cpp b/tools/qmltc/qmltctyperesolver.cpp
new file mode 100644
index 0000000000..a7bf9debac
--- /dev/null
+++ b/tools/qmltc/qmltctyperesolver.cpp
@@ -0,0 +1,56 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "qmltctyperesolver.h"
+
+#include <private/qqmljsimporter_p.h>
+#include <private/qqmljsliteralbindingcheck_p.h>
+#include <private/qv4value_p.h>
+
+#include <QtCore/qqueue.h>
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qdiriterator.h>
+
+Q_LOGGING_CATEGORY(lcTypeResolver2, "qml.qmltc.typeresolver", QtInfoMsg);
+
+void QmltcTypeResolver::init(QmltcVisitor *visitor, QQmlJS::AST::Node *program)
+{
+ QQmlJSTypeResolver::init(visitor, program);
+
+ m_root = visitor->result();
+
+ QQueue<QQmlJSScope::Ptr> objects;
+ objects.enqueue(m_root);
+ while (!objects.isEmpty()) {
+ const QQmlJSScope::Ptr object = objects.dequeue();
+ const QQmlJS::SourceLocation location = object->sourceLocation();
+ qCDebug(lcTypeResolver2()).nospace() << "inserting " << object.data() << " at "
+ << location.startLine << ':' << location.startColumn;
+ m_objectsByLocationNonConst.insert({ location.startLine, location.startColumn }, object);
+
+ const auto childScopes = object->childScopes();
+ for (const auto &childScope : childScopes)
+ objects.enqueue(childScope);
+ }
+}
+
+QQmlJSScope::Ptr
+QmltcTypeResolver::scopeForLocation(const QV4::CompiledData::Location &location) const
+{
+ qCDebug(lcTypeResolver2()).nospace()
+ << "looking for object at " << location.line() << ':' << location.column();
+ return m_objectsByLocationNonConst.value(location);
+}
+
+QPair<QString, QQmlJSScope::Ptr>
+QmltcTypeResolver::importedType(const QQmlJSScope::ConstPtr &type) const
+{
+ const auto files = m_importer->importedFiles();
+ auto it = std::find_if(files.cbegin(), files.cend(), [&](const QQmlJSScope::Ptr &importedType) {
+ return importedType.data() == type.data();
+ });
+ if (it == files.cend())
+ return {};
+ return { it.key(), it.value() };
+}
diff --git a/tools/qmltc/qmltctyperesolver.h b/tools/qmltc/qmltctyperesolver.h
new file mode 100644
index 0000000000..19a3ee01bc
--- /dev/null
+++ b/tools/qmltc/qmltctyperesolver.h
@@ -0,0 +1,40 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef QMLTCTYPERESOLVER_H
+#define QMLTCTYPERESOLVER_H
+
+#include "qmltcvisitor.h"
+
+#include <QtQml/private/qqmlirbuilder_p.h>
+#include <private/qqmljstyperesolver_p.h>
+#include <private/qqmljsimporter_p.h>
+#include <private/qqmljslogger_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QmltcTypeResolver : public QQmlJSTypeResolver
+{
+public:
+ QmltcTypeResolver(QQmlJSImporter *importer) : QQmlJSTypeResolver(importer), m_importer(importer)
+ {
+ Q_ASSERT(importer);
+ }
+
+ void init(QmltcVisitor *visitor, QQmlJS::AST::Node *program);
+
+ QQmlJSScope::Ptr scopeForLocation(const QV4::CompiledData::Location &location) const;
+
+ // returns an import pair {url, modifiable type} for a given \a type
+ QPair<QString, QQmlJSScope::Ptr> importedType(const QQmlJSScope::ConstPtr &type) const;
+
+private:
+ QQmlJSImporter *m_importer = nullptr;
+
+ QHash<QV4::CompiledData::Location, QQmlJSScope::Ptr> m_objectsByLocationNonConst;
+ QQmlJSScope::Ptr m_root;
+};
+
+QT_END_NAMESPACE
+
+#endif // QMLTCTYPERESOLVER_H
diff --git a/tools/qmltc/qmltcvisitor.cpp b/tools/qmltc/qmltcvisitor.cpp
new file mode 100644
index 0000000000..119308ef65
--- /dev/null
+++ b/tools/qmltc/qmltcvisitor.cpp
@@ -0,0 +1,871 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "qmltcvisitor.h"
+#include "qmltcpropertyutils.h"
+
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qstack.h>
+#include <QtCore/qdir.h>
+#include <QtCore/qloggingcategory.h>
+#include <QtQml/private/qqmlsignalnames_p.h>
+
+#include <private/qqmljsutils_p.h>
+
+#include <algorithm>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+Q_DECLARE_LOGGING_CATEGORY(lcQmltcCompiler)
+
+static QString uniqueNameFromPieces(const QStringList &pieces, QHash<QString, int> &repetitions)
+{
+ QString possibleName = pieces.join(u'_');
+ const int count = repetitions[possibleName]++;
+ if (count > 0)
+ possibleName.append(u"_" + QString::number(count));
+ return possibleName;
+}
+
+static bool isExplicitComponent(const QQmlJSScope::ConstPtr &type)
+{
+ if (!type->isComposite())
+ return false;
+ auto base = type->baseType();
+ return base && base->internalName() == u"QQmlComponent";
+}
+
+/*! \internal
+ Returns if type is an implicit component.
+ This method should only be called after implicit components
+ are detected, that is, after QQmlJSImportVisitor::endVisit(UiProgram *)
+ was called.
+ */
+static bool isImplicitComponent(const QQmlJSScope::ConstPtr &type)
+{
+ if (!type->isComposite())
+ return false;
+ const auto cppBase = QQmlJSScope::nonCompositeBaseType(type);
+ const bool isComponentBased = (cppBase && cppBase->internalName() == u"QQmlComponent");
+ return type->isComponentRootElement() && !isComponentBased;
+}
+
+/*! \internal
+ Checks if type is inside or a (implicit or explicit) component.
+ This method should only be called after implicit components
+ are detected, that is, after QQmlJSImportVisitor::endVisit(UiProgram *)
+ was called.
+ */
+static bool isOrUnderComponent(QQmlJSScope::ConstPtr type)
+{
+ Q_ASSERT(type->isComposite()); // we're dealing with composite types here
+ for (; type; type = type->parentScope()) {
+ if (isExplicitComponent(type) || isImplicitComponent(type)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+QmltcVisitor::QmltcVisitor(const QQmlJSScope::Ptr &target, QQmlJSImporter *importer,
+ QQmlJSLogger *logger, const QString &implicitImportDirectory,
+ const QStringList &qmldirFiles)
+ : QQmlJSImportVisitor(target, importer, logger, implicitImportDirectory, qmldirFiles)
+{
+ m_qmlTypeNames.append(QFileInfo(logger->fileName()).baseName()); // put document root
+}
+
+void QmltcVisitor::findCppIncludes()
+{
+ // TODO: this pass is slow: we have to do exhaustive search because some C++
+ // code could do forward declarations and they are hard to handle correctly
+ QSet<const QQmlJSScope *> visitedTypes; // we can still improve by walking all types only once
+ const auto visitType = [&visitedTypes](const QQmlJSScope::ConstPtr &type) -> bool {
+ if (visitedTypes.contains(type.data()))
+ return true;
+ visitedTypes.insert(type.data());
+ return false;
+ };
+ const auto addCppInclude = [this](const QQmlJSScope::ConstPtr &type) {
+ if (QString includeFile = type->filePath(); includeFile.endsWith(u".h"))
+ m_cppIncludes.insert(std::move(includeFile));
+ };
+
+ const auto findInType = [&](const QQmlJSScope::ConstPtr &type) {
+ if (!type)
+ return;
+ if (visitType(type)) // optimization - don't call nonCompositeBaseType() needlessly
+ return;
+
+ // look in type
+ addCppInclude(type);
+
+ if (type->isListProperty())
+ addCppInclude(type->valueType());
+
+ // look in type's base type
+ auto base = type->baseType();
+ if (!base && type->isComposite())
+ // in this case, qqmljsimportvisitor would have already print an error message
+ // about the missing type, so just return silently without crashing
+ return;
+ if (!base || visitType(base))
+ return;
+ addCppInclude(base);
+ };
+
+ const auto constructPrivateInclude = [](QStringView publicInclude) -> QString {
+ if (publicInclude.isEmpty())
+ return QString();
+ Q_ASSERT(publicInclude.endsWith(u".h"_s) || publicInclude.endsWith(u".hpp"_s));
+ const qsizetype dotLocation = publicInclude.lastIndexOf(u'.');
+ QStringView extension = publicInclude.sliced(dotLocation);
+ QStringView includeWithoutExtension = publicInclude.first(dotLocation);
+ // check if "public" include is in fact already private
+ if (publicInclude.startsWith(u"private"))
+ return includeWithoutExtension + u"_p" + extension;
+ return u"private/" + includeWithoutExtension + u"_p" + extension;
+ };
+
+ // walk the whole type hierarchy
+ QStack<QQmlJSScope::ConstPtr> types;
+ types.push(m_exportedRootScope);
+ while (!types.isEmpty()) {
+ auto type = types.pop();
+ Q_ASSERT(type);
+
+ const auto scopeType = type->scopeType();
+ if (scopeType != QQmlSA::ScopeType::QMLScope
+ && scopeType != QQmlSA::ScopeType::GroupedPropertyScope
+ && scopeType != QQmlSA::ScopeType::AttachedPropertyScope) {
+ continue;
+ }
+
+ for (auto t = type; !type->isArrayScope() && t; t = t->baseType()) {
+ findInType(t);
+
+ // look in properties
+ const auto properties = t->ownProperties();
+ for (const QQmlJSMetaProperty &p : properties) {
+ findInType(p.type());
+
+ if (p.isPrivate() && t->filePath().endsWith(u".h")) {
+ const QString ownersInclude = t->filePath();
+ QString privateInclude = constructPrivateInclude(ownersInclude);
+ if (!privateInclude.isEmpty())
+ m_cppIncludes.insert(std::move(privateInclude));
+ }
+ }
+
+ // look in methods
+ const auto methods = t->ownMethods();
+ for (const QQmlJSMetaMethod &m : methods) {
+ findInType(m.returnType());
+
+ const auto parameters = m.parameters();
+ for (const auto &param : parameters)
+ findInType(param.type());
+ }
+ }
+
+ types.append(type->childScopes());
+ }
+
+ // remove own include
+ m_cppIncludes.remove(m_exportedRootScope->filePath());
+}
+
+static void addCleanQmlTypeName(QStringList *names, const QQmlJSScope::ConstPtr &scope)
+{
+ Q_ASSERT(scope->scopeType() == QQmlSA::ScopeType::QMLScope);
+ Q_ASSERT(!scope->isArrayScope());
+ Q_ASSERT(!scope->baseTypeName().isEmpty());
+ // the scope is guaranteed to be a new QML type, so any prefixes (separated
+ // by dots) should be import namespaces
+ const std::optional<QString> &inlineComponentName = scope->inlineComponentName();
+ QString name = inlineComponentName ? *inlineComponentName : scope->baseTypeName();
+ names->append(name.replace(u'.', u'_'));
+}
+
+bool QmltcVisitor::visit(QQmlJS::AST::UiObjectDefinition *object)
+{
+ const bool processingRoot = !rootScopeIsValid();
+
+ if (!QQmlJSImportVisitor::visit(object))
+ return false;
+
+ if (processingRoot || m_currentScope->isInlineComponent()) {
+ Q_ASSERT(rootScopeIsValid());
+ setRootFilePath();
+ }
+
+ // we're not interested in non-QML scopes
+ if (m_currentScope->scopeType() != QQmlSA::ScopeType::QMLScope)
+ return true;
+
+ if (m_currentScope->isInlineComponent()) {
+ m_inlineComponentNames.append(m_currentRootName);
+ m_inlineComponents[m_currentRootName] = m_currentScope;
+ }
+
+ if (m_currentScope != m_exportedRootScope) // not document root
+ addCleanQmlTypeName(&m_qmlTypeNames, m_currentScope);
+ // give C++-relevant internal names to QMLScopes, we can use them later in compiler
+ m_currentScope->setInternalName(uniqueNameFromPieces(m_qmlTypeNames, m_qmlTypeNameCounts));
+ m_qmlTypesWithQmlBases[m_currentRootName].append(m_currentScope);
+
+ return true;
+}
+
+void QmltcVisitor::endVisit(QQmlJS::AST::UiObjectDefinition *object)
+{
+ if (m_currentScope->scopeType() == QQmlSA::ScopeType::QMLScope)
+ m_qmlTypeNames.removeLast();
+ QQmlJSImportVisitor::endVisit(object);
+}
+
+bool QmltcVisitor::visit(QQmlJS::AST::UiObjectBinding *uiob)
+{
+ if (!QQmlJSImportVisitor::visit(uiob))
+ return false;
+
+ if (m_currentScope != m_exportedRootScope) // not document root
+ addCleanQmlTypeName(&m_qmlTypeNames, m_currentScope);
+ // give C++-relevant internal names to QMLScopes, we can use them later in compiler
+ m_currentScope->setInternalName(uniqueNameFromPieces(m_qmlTypeNames, m_qmlTypeNameCounts));
+
+ m_qmlTypesWithQmlBases[m_currentRootName].append(m_currentScope);
+ return true;
+}
+
+void QmltcVisitor::endVisit(QQmlJS::AST::UiObjectBinding *uiob)
+{
+ m_qmlTypeNames.removeLast();
+ QQmlJSImportVisitor::endVisit(uiob);
+}
+
+bool QmltcVisitor::visit(QQmlJS::AST::UiPublicMember *publicMember)
+{
+ if (!QQmlJSImportVisitor::visit(publicMember))
+ return false;
+
+ // augment property: set its write/read/etc. methods
+ if (publicMember->type == QQmlJS::AST::UiPublicMember::Property) {
+ const auto name = publicMember->name.toString();
+
+ QQmlJSScope::Ptr owner =
+ m_savedBindingOuterScope ? m_savedBindingOuterScope : m_currentScope;
+ QQmlJSMetaProperty property = owner->ownProperty(name);
+ Q_ASSERT(property.isValid());
+ if (!property.isAlias()) { // aliases are covered separately
+ QmltcPropertyData compiledData(property);
+ if (property.read().isEmpty())
+ property.setRead(compiledData.read);
+ if (!property.isList()) {
+ if (property.write().isEmpty() && property.isWritable())
+ property.setWrite(compiledData.write);
+ // Note: prefer BINDABLE to NOTIFY
+ if (property.bindable().isEmpty())
+ property.setBindable(compiledData.bindable);
+ }
+ owner->addOwnProperty(property);
+ }
+
+ const QString notifyName = QQmlSignalNames::propertyNameToChangedSignalName(name);
+ // also check that notify is already a method of the scope
+ {
+ auto owningScope = m_savedBindingOuterScope ? m_savedBindingOuterScope : m_currentScope;
+ const auto methods = owningScope->ownMethods(notifyName);
+ if (methods.size() != 1) {
+ const QString errorString =
+ methods.isEmpty() ? u"no signal"_s : u"too many signals"_s;
+ m_logger->log(
+ u"internal error: %1 found for property '%2'"_s.arg(errorString, name),
+ qmlCompiler, publicMember->identifierToken);
+ return false;
+ } else if (methods[0].methodType() != QQmlJSMetaMethodType::Signal) {
+ m_logger->log(u"internal error: method %1 of property %2 must be a signal"_s.arg(
+ notifyName, name),
+ qmlCompiler, publicMember->identifierToken);
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool QmltcVisitor::visit(QQmlJS::AST::UiScriptBinding *scriptBinding)
+{
+ if (!QQmlJSImportVisitor::visit(scriptBinding))
+ return false;
+
+ {
+ const auto id = scriptBinding->qualifiedId;
+ if (!id->next && id->name == QLatin1String("id"))
+ m_typesWithId[m_currentScope] = -1; // temporary value
+ }
+
+ return true;
+}
+
+bool QmltcVisitor::visit(QQmlJS::AST::UiInlineComponent *component)
+{
+ if (!QQmlJSImportVisitor::visit(component))
+ return false;
+ return true;
+}
+
+void QmltcVisitor::endVisit(QQmlJS::AST::UiProgram *program)
+{
+ QQmlJSImportVisitor::endVisit(program);
+ if (!rootScopeIsValid()) // in case we failed badly
+ return;
+
+ QHash<QQmlJSScope::ConstPtr, QList<QQmlJSMetaPropertyBinding>> bindings;
+ for (const QQmlJSScope::ConstPtr &type : std::as_const(m_qmlTypes)) {
+ if (isOrUnderComponent(type))
+ continue;
+ bindings.insert(type, type->ownPropertyBindingsInQmlIROrder());
+ }
+
+ postVisitResolve(bindings);
+ setupAliases();
+
+ if (m_mode != Mode::Compile)
+ return;
+
+ findCppIncludes();
+
+ for (const QList<QQmlJSScope::ConstPtr> &qmlTypes : m_pureQmlTypes)
+ for (const QQmlJSScope::ConstPtr &type : qmlTypes)
+ checkNamesAndTypes(type);
+}
+
+QQmlJSScope::ConstPtr fetchType(const QQmlJSMetaPropertyBinding &binding)
+{
+ switch (binding.bindingType()) {
+ case QQmlSA::BindingType::Object:
+ return binding.objectType();
+ case QQmlSA::BindingType::Interceptor:
+ return binding.interceptorType();
+ case QQmlSA::BindingType::ValueSource:
+ return binding.valueSourceType();
+ // TODO: AttachedProperty and GroupProperty are not supported yet,
+ // but have to also be acknowledged here
+ default:
+ return {};
+ }
+ Q_UNREACHABLE_RETURN({});
+}
+
+template<typename Predicate>
+void iterateTypes(
+ const QQmlJSScope::ConstPtr &root,
+ const QHash<QQmlJSScope::ConstPtr, QList<QQmlJSMetaPropertyBinding>> &qmlIrOrderedBindings,
+ Predicate predicate)
+{
+ // NB: depth-first-search is used here to mimic various QmlIR passes
+ QStack<QQmlJSScope::ConstPtr> types;
+ types.push(root);
+ while (!types.isEmpty()) {
+ auto current = types.pop();
+
+ if (predicate(current))
+ continue;
+
+ if (isOrUnderComponent(current)) // ignore implicit/explicit components
+ continue;
+
+ Q_ASSERT(qmlIrOrderedBindings.contains(current));
+ const auto &bindings = qmlIrOrderedBindings[current];
+ // reverse the binding order here, because stack processes right-most
+ // child first and we need left-most first
+ for (auto it = bindings.rbegin(); it != bindings.rend(); ++it) {
+ const auto &binding = *it;
+ if (auto type = fetchType(binding))
+ types.push(type);
+ }
+ }
+}
+
+template<typename Predicate>
+void iterateBindings(
+ const QQmlJSScope::ConstPtr &root,
+ const QHash<QQmlJSScope::ConstPtr, QList<QQmlJSMetaPropertyBinding>> &qmlIrOrderedBindings,
+ Predicate predicate)
+{
+ // NB: depth-first-search is used here to mimic various QmlIR passes
+ QStack<QQmlJSScope::ConstPtr> types;
+ types.push(root);
+ while (!types.isEmpty()) {
+ auto current = types.pop();
+
+ if (isOrUnderComponent(current)) // ignore implicit/explicit components
+ continue;
+
+ Q_ASSERT(qmlIrOrderedBindings.contains(current));
+ const auto &bindings = qmlIrOrderedBindings[current];
+ // reverse the binding order here, because stack processes right-most
+ // child first and we need left-most first
+ for (auto it = bindings.rbegin(); it != bindings.rend(); ++it) {
+ const auto &binding = *it;
+
+ if (predicate(current, binding))
+ continue;
+
+ if (auto type = fetchType(binding))
+ types.push(type);
+ }
+ }
+}
+
+/*! \internal
+ This is a special function that must be called after
+ QQmlJSImportVisitor::endVisit(QQmlJS::AST::UiProgram *). It is used to
+ resolve things that couldn't be resolved during the AST traversal, such
+ as anything that is dependent on implicit or explicit components
+*/
+void QmltcVisitor::postVisitResolve(
+ const QHash<QQmlJSScope::ConstPtr, QList<QQmlJSMetaPropertyBinding>> &qmlIrOrderedBindings)
+{
+
+ // add the document root (that is not an inline component), as we usually
+ // want to iterate on all inline components, followed by the document root
+ m_inlineComponentNames.append(RootDocumentNameType());
+ m_inlineComponents[RootDocumentNameType()] = m_exportedRootScope;
+
+ // match scopes to indices of QmlIR::Object from QmlIR::Document
+ qsizetype count = 0;
+ const auto setIndex = [&](const QQmlJSScope::Ptr &current) {
+ if (current->scopeType() != QQmlSA::ScopeType::QMLScope || current->isArrayScope())
+ return;
+ Q_ASSERT(!m_qmlIrObjectIndices.contains(current));
+ m_qmlIrObjectIndices[current] = count;
+ ++count;
+ };
+ QQmlJSUtils::traverseFollowingQmlIrObjectStructure(m_exportedRootScope, setIndex);
+
+ // find types that are part of the deferred bindings (we care about *types*
+ // exclusively here)
+ QSet<QQmlJSScope::ConstPtr> deferredTypes;
+ const auto findDeferred = [&](const QQmlJSScope::ConstPtr &type,
+ const QQmlJSMetaPropertyBinding &binding) {
+ const QString propertyName = binding.propertyName();
+ Q_ASSERT(!propertyName.isEmpty());
+ if (type->isNameDeferred(propertyName)) {
+ m_typesWithDeferredBindings.insert(type);
+
+ if (binding.hasObject() || binding.hasInterceptor() || binding.hasValueSource()) {
+ deferredTypes.insert(fetchType(binding));
+ return true;
+ }
+ }
+ return false;
+ };
+ for (const auto &inlineComponentName : m_inlineComponentNames) {
+ iterateBindings(m_inlineComponents[inlineComponentName], qmlIrOrderedBindings,
+ findDeferred);
+ }
+
+ const auto isOrUnderDeferred = [&deferredTypes](QQmlJSScope::ConstPtr type) {
+ for (; type; type = type->parentScope()) {
+ if (deferredTypes.contains(type))
+ return true;
+ }
+ return false;
+ };
+
+ // find all "pure" QML types
+ QList<QQmlJSScope::ConstPtr> explicitComponents;
+ for (qsizetype i = 0; i < m_qmlTypes.size(); ++i) {
+ const QQmlJSScope::ConstPtr &type = m_qmlTypes.at(i);
+
+ if (isOrUnderComponent(type) || isOrUnderDeferred(type)) {
+ // root is special: we compile Component roots. root is also never
+ // deferred, so in case `isOrUnderDeferred(type)` returns true, we
+ // always continue here
+ if (type != m_exportedRootScope) {
+ // if a type is an explicit component, its id "leaks" into the
+ // document context
+ if (isExplicitComponent(type))
+ explicitComponents.append(type);
+ continue;
+ }
+ }
+
+ const InlineComponentOrDocumentRootName inlineComponent =
+ type->enclosingInlineComponentName();
+ QList<QQmlJSScope::ConstPtr> &pureQmlTypes = m_pureQmlTypes[inlineComponent];
+ m_creationIndices[type] = pureQmlTypes.size();
+ pureQmlTypes.append(type);
+ }
+
+ // update the typeCounts
+ for (const auto &inlineComponent : m_inlineComponentNames) {
+ m_inlineComponentTypeCount[inlineComponent] = m_pureQmlTypes[inlineComponent].size();
+ }
+
+ // add explicit components to the object creation indices
+ {
+ QHash<InlineComponentOrDocumentRootName, qsizetype> index;
+ for (const QQmlJSScope::ConstPtr &c : std::as_const(explicitComponents)) {
+ const InlineComponentOrDocumentRootName inlineComponent =
+ c->enclosingInlineComponentName();
+ m_creationIndices[c] =
+ m_pureQmlTypes[inlineComponent].size() + index[inlineComponent]++;
+ m_inlineComponentTypeCount[inlineComponent]++;
+ }
+ }
+
+ // m_qmlTypesWithQmlBases should contain the types to be compiled.
+ // Some types should not be compiled and are therefore filtered out:
+ // * deferred types
+ // * types inside of capital-c-Components (implicit and explicit)
+ // * non-composite types (that is, types not defined in qml)
+ //
+ // This can not be done earlier as implicitly wrapped Components are
+ // only known after visitation is over!
+
+ // filter step:
+ for (const auto &inlineComponent : m_inlineComponentNames) {
+ QList<QQmlJSScope::ConstPtr> filteredQmlTypesWithQmlBases;
+ QList<QQmlJSScope::ConstPtr> &unfilteredQmlTypesWithQmlBases =
+ m_qmlTypesWithQmlBases[inlineComponent];
+ filteredQmlTypesWithQmlBases.reserve(unfilteredQmlTypesWithQmlBases.size());
+ std::copy_if(unfilteredQmlTypesWithQmlBases.cbegin(), unfilteredQmlTypesWithQmlBases.cend(),
+ std::back_inserter(filteredQmlTypesWithQmlBases),
+ [&](const QQmlJSScope::ConstPtr &type) {
+ auto base = type->baseType();
+ return base && base->isComposite() && !isOrUnderComponent(type)
+ && !isOrUnderDeferred(type);
+ });
+ qSwap(unfilteredQmlTypesWithQmlBases, filteredQmlTypesWithQmlBases);
+ }
+
+ // count QmlIR::Objects in the document - the amount is used to calculate
+ // object indices of implicit components
+ QHash<InlineComponentOrDocumentRootName, qsizetype> qmlScopeCount;
+ const auto countQmlScopes = [&](const QQmlJSScope::ConstPtr &scope) {
+ if (scope->isArrayScope()) // special kind of QQmlJSScope::QMLScope
+ return;
+ switch (scope->scopeType()) {
+ case QQmlSA::ScopeType::QMLScope:
+ case QQmlSA::ScopeType::GroupedPropertyScope:
+ case QQmlSA::ScopeType::AttachedPropertyScope: {
+ ++qmlScopeCount[scope->enclosingInlineComponentName()];
+ break;
+ }
+ default:
+ return;
+ }
+ return;
+ };
+ // order doesn't matter (so re-use QQmlJSUtils)
+ QQmlJSUtils::traverseFollowingQmlIrObjectStructure(m_exportedRootScope, countQmlScopes);
+
+ // figure synthetic indices of QQmlComponent-wrapped types
+ int syntheticCreationIndex;
+ const auto addSyntheticIndex = [&](const QQmlJSScope::ConstPtr &type) {
+ // explicit component
+ if (isExplicitComponent(type)) {
+ m_syntheticTypeIndices[type] = m_qmlIrObjectIndices.value(type, -1);
+ return true;
+ }
+ // implicit component
+ if (isImplicitComponent(type)) {
+ const int index =
+ qmlScopeCount[type->enclosingInlineComponentName()] + syntheticCreationIndex++;
+ m_syntheticTypeIndices[type] = index;
+ return true;
+ }
+ return false;
+ };
+
+ for (const auto &inlineComponentName : m_inlineComponentNames) {
+ syntheticCreationIndex = 0; // reset for each inline component
+ iterateTypes(m_inlineComponents[inlineComponentName], qmlIrOrderedBindings,
+ addSyntheticIndex);
+ }
+
+ // figure runtime object ids for non-component wrapped types
+ int currentId;
+ const auto setRuntimeId = [&](const QQmlJSScope::ConstPtr &type) {
+ // any type wrapped in an implicit component shouldn't be processed
+ // here. even if it has id, it doesn't need to be set by qmltc
+ if (type->isComponentRootElement()) {
+ return true;
+ }
+
+ if (m_typesWithId.contains(type)) {
+ m_typesWithId[type] = currentId++;
+ }
+
+ return false;
+ };
+
+ for (const auto &inlineComponentName : m_inlineComponentNames) {
+ currentId = 0; // reset for each inline component
+ iterateTypes(m_inlineComponents[inlineComponentName], qmlIrOrderedBindings, setRuntimeId);
+ }
+}
+
+static void setAliasData(QQmlJSMetaProperty *alias, const QQmlJSUtils::ResolvedAlias &origin)
+{
+ Q_ASSERT(origin.kind != QQmlJSUtils::AliasTarget_Invalid);
+ QmltcPropertyData compiledData(*alias);
+ if (alias->read().isEmpty())
+ alias->setRead(compiledData.read);
+ if (origin.kind == QQmlJSUtils::AliasTarget_Object) // id-pointing aliases only have READ method
+ return;
+ if (origin.property.isWritable() && alias->write().isEmpty())
+ alias->setWrite(compiledData.write);
+
+ // the engine always compiles a notify for properties/aliases defined in qml code
+ // Yes, this generated notify will never be emitted.
+ if (alias->notify().isEmpty())
+ alias->setNotify(compiledData.notify);
+
+ if (!origin.property.bindable().isEmpty() && alias->bindable().isEmpty())
+ alias->setBindable(compiledData.bindable);
+}
+
+void QmltcVisitor::setupAliases()
+{
+ QStack<QQmlJSScope::Ptr> types;
+ types.push(m_exportedRootScope);
+
+ while (!types.isEmpty()) {
+ QQmlJSScope::Ptr current = types.pop();
+ auto properties = current->ownProperties();
+
+ for (QQmlJSMetaProperty &p : properties) {
+ if (!p.isAlias())
+ continue;
+
+ auto result = QQmlJSUtils::resolveAlias(m_scopesById, p, current,
+ QQmlJSUtils::AliasResolutionVisitor {});
+ if (result.kind == QQmlJSUtils::AliasTarget_Invalid) {
+ m_logger->log(QStringLiteral("Cannot resolve alias \"%1\"").arg(p.propertyName()),
+ qmlUnresolvedAlias, current->sourceLocation());
+ continue;
+ }
+ setAliasData(&p, result);
+ current->addOwnProperty(p);
+ }
+ }
+}
+
+void QmltcVisitor::checkNamesAndTypes(const QQmlJSScope::ConstPtr &type)
+{
+ static const QString cppKeywords[] = {
+ u"alignas"_s,
+ u"alignof"_s,
+ u"and"_s,
+ u"and_eq"_s,
+ u"asm"_s,
+ u"atomic_cancel"_s,
+ u"atomic_commit"_s,
+ u"atomic_noexcept"_s,
+ u"auto"_s,
+ u"bitand"_s,
+ u"bitor"_s,
+ u"bool"_s,
+ u"break"_s,
+ u"case"_s,
+ u"catch"_s,
+ u"char"_s,
+ u"char16_t"_s,
+ u"char32_t"_s,
+ u"char8_t"_s,
+ u"class"_s,
+ u"co_await"_s,
+ u"co_return"_s,
+ u"co_yield"_s,
+ u"compl"_s,
+ u"concept"_s,
+ u"const"_s,
+ u"const_cast"_s,
+ u"consteval"_s,
+ u"constexpr"_s,
+ u"continue"_s,
+ u"decltype"_s,
+ u"default"_s,
+ u"delete"_s,
+ u"do"_s,
+ u"double"_s,
+ u"dynamic_cast"_s,
+ u"else"_s,
+ u"enum"_s,
+ u"explicit"_s,
+ u"export"_s,
+ u"extern"_s,
+ u"false"_s,
+ u"float"_s,
+ u"for"_s,
+ u"friend"_s,
+ u"goto"_s,
+ u"if"_s,
+ u"inline"_s,
+ u"int"_s,
+ u"long"_s,
+ u"mutable"_s,
+ u"namespace"_s,
+ u"new"_s,
+ u"noexcept"_s,
+ u"not"_s,
+ u"not_eq"_s,
+ u"nullptr"_s,
+ u"operator"_s,
+ u"or"_s,
+ u"or_eq"_s,
+ u"private"_s,
+ u"protected"_s,
+ u"public"_s,
+ u"reflexpr"_s,
+ u"register"_s,
+ u"reinterpret_cast"_s,
+ u"requires"_s,
+ u"return"_s,
+ u"short"_s,
+ u"signed"_s,
+ u"sizeof"_s,
+ u"static"_s,
+ u"static_assert"_s,
+ u"static_cast"_s,
+ u"struct"_s,
+ u"switch"_s,
+ u"synchronized"_s,
+ u"template"_s,
+ u"this"_s,
+ u"thread_local"_s,
+ u"throw"_s,
+ u"true"_s,
+ u"try"_s,
+ u"typedef"_s,
+ u"typeid"_s,
+ u"typename"_s,
+ u"union"_s,
+ u"unsigned"_s,
+ u"using"_s,
+ u"virtual"_s,
+ u"void"_s,
+ u"volatile"_s,
+ u"wchar_t"_s,
+ u"while"_s,
+ u"xor"_s,
+ u"xor_eq"_s,
+ };
+ Q_ASSERT(std::is_sorted(std::begin(cppKeywords), std::end(cppKeywords)));
+
+ const auto isReserved = [&](QStringView word) {
+ if (word.startsWith(QChar(u'_')) && word.size() >= 2
+ && (word[1].isUpper() || word[1] == QChar(u'_'))) {
+ return true; // Identifiers starting with underscore and uppercase are reserved in C++
+ }
+ return std::binary_search(std::begin(cppKeywords), std::end(cppKeywords), word);
+ };
+
+ const auto validate = [&](QStringView name, QStringView errorPrefix) {
+ if (!isReserved(name))
+ return;
+ m_logger->log(errorPrefix + u" '" + name + u"' is a reserved C++ word, consider renaming",
+ qmlCompiler, type->sourceLocation());
+ };
+
+ const auto validateType = [&type, this](const QQmlJSScope::ConstPtr &typeToCheck,
+ QStringView name, QStringView errorPrefix) {
+ if (type->moduleName().isEmpty() || typeToCheck.isNull())
+ return;
+
+ if (typeToCheck->isComposite() && typeToCheck->moduleName() != type->moduleName()) {
+ m_logger->log(
+ QStringLiteral(
+ "Can't compile the %1 type \"%2\" to C++ because it "
+ "lives in \"%3\" instead of the current file's \"%4\" QML module.")
+ .arg(errorPrefix, name, typeToCheck->moduleName(), type->moduleName()),
+ qmlCompiler, type->sourceLocation());
+ }
+ };
+
+ validateType(type->baseType(), type->baseTypeName(), u"QML base");
+
+ const auto enums = type->ownEnumerations();
+ for (auto it = enums.cbegin(); it != enums.cend(); ++it) {
+ const QQmlJSMetaEnum e = it.value();
+ validate(e.name(), u"Enumeration");
+
+ const auto enumKeys = e.keys();
+ for (const auto &key : enumKeys)
+ validate(key, u"Enumeration '%1' key"_s.arg(e.name()));
+ }
+
+ const auto properties = type->ownProperties();
+ for (auto it = properties.cbegin(); it != properties.cend(); ++it) {
+ const QQmlJSMetaProperty &p = it.value();
+ validate(p.propertyName(), u"Property");
+
+ if (!p.isAlias() && !p.typeName().isEmpty())
+ validateType(p.type(), p.typeName(), u"QML property");
+ }
+
+ const auto methods = type->ownMethods();
+ for (auto it = methods.cbegin(); it != methods.cend(); ++it) {
+ const QQmlJSMetaMethod &m = it.value();
+ validate(m.methodName(), u"Method");
+ if (!m.returnTypeName().isEmpty())
+ validateType(m.returnType(), m.returnTypeName(), u"QML method return");
+
+ for (const auto &parameter : m.parameters()) {
+ validate(parameter.name(), u"Method '%1' parameter"_s.arg(m.methodName()));
+ if (!parameter.typeName().isEmpty())
+ validateType(parameter.type(), parameter.typeName(), u"QML parameter");
+ }
+ }
+
+ // TODO: one could also test signal handlers' parameters but we do not store
+ // this information in QQmlJSMetaPropertyBinding currently
+}
+
+/*! \internal
+ * Sets the file paths for the document and the inline components roots.
+ */
+void QmltcVisitor::setRootFilePath()
+{
+ const QString filePath = m_currentScope->filePath();
+ if (filePath.endsWith(u".h")) // assume the correct path is set
+ return;
+ Q_ASSERT(filePath.endsWith(u".qml"_s));
+
+ const QString correctedFilePath = sourceDirectoryPath(filePath);
+ const QStringList paths = m_importer->resourceFileMapper()->resourcePaths(
+ QQmlJSResourceFileMapper::localFileFilter(correctedFilePath));
+ auto firstHeader = std::find_if(paths.cbegin(), paths.cend(),
+ [](const QString &x) { return x.endsWith(u".h"_s); });
+ if (firstHeader == paths.cend()) {
+ const QString matchedPaths = paths.isEmpty() ? u"<none>"_s : paths.join(u", ");
+ qCDebug(lcQmltcCompiler,
+ "Failed to find a header file name for path %s. Paths checked:\n%s",
+ correctedFilePath.toUtf8().constData(), matchedPaths.toUtf8().constData());
+ return;
+ }
+ // NB: get the file name to avoid prefixes
+ m_currentScope->setFilePath(QFileInfo(*firstHeader).fileName());
+}
+
+QString QmltcVisitor::sourceDirectoryPath(const QString &path)
+{
+ auto result = QQmlJSUtils::sourceDirectoryPath(m_importer, path);
+ if (const QString *srcDirPath = std::get_if<QString>(&result))
+ return *srcDirPath;
+
+ const QQmlJS::DiagnosticMessage *error = std::get_if<QQmlJS::DiagnosticMessage>(&result);
+ Q_ASSERT(error);
+ qCDebug(lcQmltcCompiler, "%s", error->message.toUtf8().constData());
+ // return input as a fallback
+ return path;
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qmltc/qmltcvisitor.h b/tools/qmltc/qmltcvisitor.h
new file mode 100644
index 0000000000..111df0e885
--- /dev/null
+++ b/tools/qmltc/qmltcvisitor.h
@@ -0,0 +1,191 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef QMLTCVISITOR_H
+#define QMLTCVISITOR_H
+
+#include <QtCore/qstring.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/qlist.h>
+
+#include <QtQml/private/qqmlirbuilder_p.h>
+#include <private/qqmljsimportvisitor_p.h>
+#include <private/qqmljslogger_p.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+class QmltcVisitor : public QQmlJSImportVisitor
+{
+ void findCppIncludes();
+ void postVisitResolve(const QHash<QQmlJSScope::ConstPtr, QList<QQmlJSMetaPropertyBinding>>
+ &qmlIrOrderedBindings);
+ void setupAliases();
+ void checkNamesAndTypes(const QQmlJSScope::ConstPtr &type);
+ void setRootFilePath();
+
+ QString sourceDirectoryPath(const QString &path);
+
+ using InlineComponentOrDocumentRootName = QQmlJSScope::InlineComponentOrDocumentRootName;
+
+public:
+ QmltcVisitor(const QQmlJSScope::Ptr &target, QQmlJSImporter *importer, QQmlJSLogger *logger,
+ const QString &implicitImportDirectory,
+ const QStringList &qmldirFiles = QStringList());
+
+ bool visit(QQmlJS::AST::UiObjectDefinition *) override;
+ void endVisit(QQmlJS::AST::UiObjectDefinition *) override;
+
+ bool visit(QQmlJS::AST::UiObjectBinding *) override;
+ void endVisit(QQmlJS::AST::UiObjectBinding *) override;
+
+ bool visit(QQmlJS::AST::UiScriptBinding *) override;
+
+ bool visit(QQmlJS::AST::UiPublicMember *) override;
+
+ bool visit(QQmlJS::AST::UiInlineComponent *) override;
+
+ void endVisit(QQmlJS::AST::UiProgram *) override;
+
+ QList<QQmlJSScope::ConstPtr>
+ qmlTypesWithQmlBases(const InlineComponentOrDocumentRootName &inlinedComponentName) const
+ {
+ return m_qmlTypesWithQmlBases.value(inlinedComponentName);
+ }
+ QSet<QString> cppIncludeFiles() const { return m_cppIncludes; }
+
+ qsizetype creationIndex(const QQmlJSScope::ConstPtr &type) const
+ {
+ Q_ASSERT(type->scopeType() == QQmlSA::ScopeType::QMLScope);
+ return m_creationIndices.value(type, -1);
+ }
+
+ qsizetype typeCount(const InlineComponentOrDocumentRootName &inlineComponent) const
+ {
+ return m_inlineComponentTypeCount.value(inlineComponent);
+ }
+
+ qsizetype qmlComponentIndex(const QQmlJSScope::ConstPtr &type) const
+ {
+ Q_ASSERT(type->scopeType() == QQmlSA::ScopeType::QMLScope);
+ return m_syntheticTypeIndices.value(type, -1);
+ }
+
+ qsizetype qmlIrObjectIndex(const QQmlJSScope::ConstPtr &type) const
+ {
+ Q_ASSERT(type->scopeType() == QQmlSA::ScopeType::QMLScope);
+ Q_ASSERT(m_qmlIrObjectIndices.contains(type));
+ return m_qmlIrObjectIndices.value(type, -1);
+ }
+
+ /*! \internal
+ Returns a runtime index counterpart of `id: foo` for \a type. Returns -1
+ if \a type does not have an id.
+ */
+ int runtimeId(const QQmlJSScope::ConstPtr &type) const
+ {
+ // NB: this function is expected to be called for "pure" types
+ Q_ASSERT(!m_typesWithId.contains(type) || m_typesWithId[type] != -1);
+ return m_typesWithId.value(type, -1);
+ }
+
+ /*! \internal
+ Returns all encountered QML types.
+ */
+ QList<QQmlJSScope::ConstPtr> allQmlTypes() const { return qmlTypes(); }
+
+ /*! \internal
+ Returns QML types which return \c false in
+ \c{isComponentRootElement()}. The QHash key are the enclosing inline component
+ or the root document name when not beloning to any inline component.
+ Called "pure", because these are the ones
+ that are not wrapped into QQmlComponent. Pure QML types can be created
+ through direct constructor invocation.
+ */
+ QList<QQmlJSScope::ConstPtr>
+ pureQmlTypes(const InlineComponentOrDocumentRootName &inlineComponent) const
+ {
+ return m_pureQmlTypes[inlineComponent];
+ }
+
+ /*!
+ * \internal
+ * Returns a list of the inline components. This list ends with the document root.
+ */
+ QList<InlineComponentOrDocumentRootName> inlineComponentNames() const
+ {
+ return m_inlineComponentNames;
+ }
+ QQmlJSScope::ConstPtr
+ inlineComponent(const InlineComponentOrDocumentRootName &inlineComponentName) const
+ {
+ return m_inlineComponents.value(inlineComponentName);
+ }
+
+ /*! \internal
+ Returns \c true when \a type has deferred bindings. Returns \c false
+ otherwise.
+ */
+ bool hasDeferredBindings(const QQmlJSScope::ConstPtr &type) const
+ {
+ return m_typesWithDeferredBindings.contains(type);
+ }
+
+ enum Mode { Import, Compile };
+ void setMode(Mode mode) { m_mode = mode; }
+
+protected:
+ QStringList m_qmlTypeNames; // names of QML types arranged as a stack
+ QHash<QString, int> m_qmlTypeNameCounts;
+ /*!
+ * \internal
+ * QML types with composite/QML base types, mapped from inline component name to types
+ */
+ QHash<InlineComponentOrDocumentRootName, QList<QQmlJSScope::ConstPtr>> m_qmlTypesWithQmlBases;
+ QSet<QString> m_cppIncludes; // all C++ includes found from QQmlJSScope hierarchy
+ QHash<InlineComponentOrDocumentRootName, QList<QQmlJSScope::ConstPtr>>
+ m_pureQmlTypes; // the ones not under QQmlComponent
+ /*!
+ * \internal
+ * List of the names of the inline components, useful when iterating over QHash that
+ * uses those names as keys. Ends with the the document root.
+ */
+ QList<InlineComponentOrDocumentRootName> m_inlineComponentNames;
+ /*!
+ * \internal
+ * Map inline component names to the corresponding type, and the document root
+ * name to all types not belonging to any inline component.
+ */
+ QHash<InlineComponentOrDocumentRootName, QQmlJSScope::Ptr> m_inlineComponents;
+ /*!
+ * \internal
+ * Map types to their creation indices. Childrens are stored at their creation index in
+ * a QObject* array either in the document root or in the inline component they belong to.
+ * Therefore two types in the same file might have the same creation index, if they belong
+ * to different inline components.
+ */
+ QHash<QQmlJSScope::ConstPtr, qsizetype> m_creationIndices;
+ /*!
+ * \internal
+ * Counts the types (pure qml types and explicit/implicit components) per inline component.
+ * Needed to set the size of the QObject* array in the document root or the inline component
+ * they belong to.
+ */
+ QHash<InlineComponentOrDocumentRootName, qsizetype> m_inlineComponentTypeCount;
+ QHash<QQmlJSScope::ConstPtr, qsizetype> m_syntheticTypeIndices;
+ QHash<QQmlJSScope::ConstPtr, qsizetype> m_qmlIrObjectIndices;
+
+ QSet<QQmlJSScope::ConstPtr> m_typesWithDeferredBindings;
+
+ // prefer allQmlTypes or pureQmlTypes. this function is misleading in qmltc
+ QList<QQmlJSScope::ConstPtr> qmlTypes() const { return QQmlJSImportVisitor::qmlTypes(); }
+
+ QHash<QQmlJSScope::ConstPtr, int> m_typesWithId;
+
+ Mode m_mode = Mode::Import;
+};
+
+QT_END_NAMESPACE
+
+#endif // QMLTCVISITOR_H
diff --git a/tools/qmltestrunner/.prev_CMakeLists.txt b/tools/qmltestrunner/.prev_CMakeLists.txt
deleted file mode 100644
index 9d55cb5075..0000000000
--- a/tools/qmltestrunner/.prev_CMakeLists.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-# Generated from qmltestrunner.pro.
-
-#####################################################################
-## qmltestrunner Tool:
-#####################################################################
-
-qt_get_tool_target_name(target_name qmltestrunner)
-qt_internal_add_tool(${target_name}
- TARGET_DESCRIPTION "QML Test Runner"
- SOURCES
- main.cpp
- PUBLIC_LIBRARIES
- Qt::Gui
- Qt::Qml
- Qt::QuickTest
-)
-
-#### Keys ignored in scope 1:.:.:qmltestrunner.pro:<TRUE>:
-# QMAKE_TARGET_DESCRIPTION = "QML" "Test" "Runner"
diff --git a/tools/qmltestrunner/CMakeLists.txt b/tools/qmltestrunner/CMakeLists.txt
index ebdec60bb5..0823ca0f6d 100644
--- a/tools/qmltestrunner/CMakeLists.txt
+++ b/tools/qmltestrunner/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qmltestrunner.pro.
#####################################################################
@@ -10,11 +13,12 @@ qt_internal_add_tool(${target_name}
TOOLS_TARGET Qml # special case
SOURCES
main.cpp
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Gui
Qt::Qml
Qt::QuickTest
)
+qt_internal_return_unless_building_tools()
#### Keys ignored in scope 1:.:.:qmltestrunner.pro:<TRUE>:
# QMAKE_TARGET_DESCRIPTION = "QML" "Test" "Runner"
diff --git a/tools/qmltestrunner/main.cpp b/tools/qmltestrunner/main.cpp
index 2463757282..d4d05a2464 100644
--- a/tools/qmltestrunner/main.cpp
+++ b/tools/qmltestrunner/main.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QtQuickTest/quicktest.h>
#include <QtCore/qstring.h>
diff --git a/tools/qmltime/.prev_CMakeLists.txt b/tools/qmltime/.prev_CMakeLists.txt
deleted file mode 100644
index 71a1d76666..0000000000
--- a/tools/qmltime/.prev_CMakeLists.txt
+++ /dev/null
@@ -1,32 +0,0 @@
-# Generated from qmltime.pro.
-
-#####################################################################
-## qmltime Tool:
-#####################################################################
-
-qt_get_tool_target_name(target_name qmltime)
-qt_internal_add_tool(${target_name}
- TARGET_DESCRIPTION "QML Time"
- SOURCES
- qmltime.cpp qmltime.h
- PUBLIC_LIBRARIES
- Qt::Gui
- Qt::Qml
- Qt::Quick
- Qt::QuickPrivate
-)
-
-#### Keys ignored in scope 1:.:.:qmltime.pro:<TRUE>:
-# QMAKE_TARGET_DESCRIPTION = "QML" "Time"
-# QML_IMPORT_NAME = "QmlTime"
-# QML_IMPORT_VERSION = "1.0"
-
-## Scopes:
-#####################################################################
-
-set_target_properties(${target_name} PROPERTIES
- QT_QML_MODULE_VERSION 1.0
- QT_QML_MODULE_URI QmlTime
-)
-
-qt6_qml_type_registration(${target_name})
diff --git a/tools/qmltime/CMakeLists.txt b/tools/qmltime/CMakeLists.txt
index 1486885e33..e3c62e2cf9 100644
--- a/tools/qmltime/CMakeLists.txt
+++ b/tools/qmltime/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# Generated from qmltime.pro.
#####################################################################
@@ -10,29 +13,17 @@ qt_internal_add_tool(${target_name}
TOOLS_TARGET Qml # special case
SOURCES
qmltime.cpp qmltime.h
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Gui
Qt::Qml
Qt::Quick
Qt::QuickPrivate
)
+qt_internal_return_unless_building_tools()
-#### Keys ignored in scope 1:.:.:qmltime.pro:<TRUE>:
-# QMAKE_TARGET_DESCRIPTION = "QML" "Time"
-# QML_IMPORT_NAME = "QmlTime"
-# QML_IMPORT_VERSION = "1.0"
-
-## Scopes:
-#####################################################################
-
-# Don't set properties on a host tool when cross compiling, because it
-# is not being built.
-if(NOT CMAKE_CROSSCOMPILING OR QT_BUILD_TOOLS_WHEN_CROSSCOMPILING)
-
-set_target_properties(${target_name} PROPERTIES
- QT_QML_MODULE_VERSION 1.0
- QT_QML_MODULE_URI QmlTime
+# Turn the tool into its own self-contained qml module
+qt_internal_add_qml_module(${target_name}
+ URI QmlTime
+ VERSION 1.0
+ NO_PLUGIN
)
-qt6_qml_type_registration(${target_name})
-endif()
-
diff --git a/tools/qmltime/example.qml b/tools/qmltime/example.qml
index e717a074e1..5333c539c6 100644
--- a/tools/qmltime/example.qml
+++ b/tools/qmltime/example.qml
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import QtQuick 2.0
import QmlTime 1.0 as QmlTime
diff --git a/tools/qmltime/linelaidout.qml b/tools/qmltime/linelaidout.qml
index e8ac636ed6..e0a129e4da 100644
--- a/tools/qmltime/linelaidout.qml
+++ b/tools/qmltime/linelaidout.qml
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import QtQuick 2.0
import QmlTime 1.0 as QmlTime
diff --git a/tools/qmltime/qmltime.cpp b/tools/qmltime/qmltime.cpp
index ca10fdde53..d36d2953d1 100644
--- a/tools/qmltime/qmltime.cpp
+++ b/tools/qmltime/qmltime.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "qmltime.h"
diff --git a/tools/qmltime/qmltime.h b/tools/qmltime/qmltime.h
index a23dc902e2..fa0f44a5e4 100644
--- a/tools/qmltime/qmltime.h
+++ b/tools/qmltime/qmltime.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef QMLTIME_H
#define QMLTIME_H
@@ -63,6 +38,5 @@ private:
QQuickView m_view;
QQuickItem *m_item;
};
-QML_DECLARE_TYPE(Timer);
#endif // QMLTIME_H
diff --git a/tools/qmltime/tests/anchors/empty.qml b/tools/qmltime/tests/anchors/empty.qml
index e6a24a83ca..b25ab5da67 100644
--- a/tools/qmltime/tests/anchors/empty.qml
+++ b/tools/qmltime/tests/anchors/empty.qml
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import QtQuick 2.0
import QmlTime 1.0 as QmlTime
diff --git a/tools/qmltime/tests/anchors/fill.qml b/tools/qmltime/tests/anchors/fill.qml
index 121eb404fc..bdc6db435f 100644
--- a/tools/qmltime/tests/anchors/fill.qml
+++ b/tools/qmltime/tests/anchors/fill.qml
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import QtQuick 2.0
import QmlTime 1.0 as QmlTime
diff --git a/tools/qmltime/tests/anchors/null.qml b/tools/qmltime/tests/anchors/null.qml
index 391aac89e3..02602d2c8c 100644
--- a/tools/qmltime/tests/anchors/null.qml
+++ b/tools/qmltime/tests/anchors/null.qml
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import QtQuick 2.0
import QmlTime 1.0 as QmlTime
diff --git a/tools/qmltime/tests/animation/large.qml b/tools/qmltime/tests/animation/large.qml
index 2f7e86cada..435a8fbe29 100644
--- a/tools/qmltime/tests/animation/large.qml
+++ b/tools/qmltime/tests/animation/large.qml
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import QtQuick 2.0
import QmlTime 1.0 as QmlTime
diff --git a/tools/qmltime/tests/animation/largeNoProps.qml b/tools/qmltime/tests/animation/largeNoProps.qml
index 504953b9e6..60baf7fb08 100644
--- a/tools/qmltime/tests/animation/largeNoProps.qml
+++ b/tools/qmltime/tests/animation/largeNoProps.qml
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import QtQuick 2.0
import QmlTime 1.0 as QmlTime
diff --git a/tools/qmltime/tests/item_creation/children.qml b/tools/qmltime/tests/item_creation/children.qml
index 73d9b6d364..ad7c271514 100644
--- a/tools/qmltime/tests/item_creation/children.qml
+++ b/tools/qmltime/tests/item_creation/children.qml
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import QtQuick 2.0
import QmlTime 1.0 as QmlTime
diff --git a/tools/qmltime/tests/item_creation/data.qml b/tools/qmltime/tests/item_creation/data.qml
index 40f2abfc7b..51f74ab1e0 100644
--- a/tools/qmltime/tests/item_creation/data.qml
+++ b/tools/qmltime/tests/item_creation/data.qml
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import QtQuick 2.0
import QmlTime 1.0 as QmlTime
diff --git a/tools/qmltime/tests/item_creation/no_creation.qml b/tools/qmltime/tests/item_creation/no_creation.qml
index 9149303137..7a15575392 100644
--- a/tools/qmltime/tests/item_creation/no_creation.qml
+++ b/tools/qmltime/tests/item_creation/no_creation.qml
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import QtQuick 2.0
import QmlTime 1.0 as QmlTime
diff --git a/tools/qmltime/tests/item_creation/resources.qml b/tools/qmltime/tests/item_creation/resources.qml
index 7afcacc892..6e920a8da4 100644
--- a/tools/qmltime/tests/item_creation/resources.qml
+++ b/tools/qmltime/tests/item_creation/resources.qml
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import QtQuick 2.0
import QmlTime 1.0 as QmlTime
diff --git a/tools/qmltime/tests/loader/Loaded.qml b/tools/qmltime/tests/loader/Loaded.qml
index 946933bafa..af41f4ef19 100644
--- a/tools/qmltime/tests/loader/Loaded.qml
+++ b/tools/qmltime/tests/loader/Loaded.qml
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import QtQuick 2.0
diff --git a/tools/qmltime/tests/loader/component_loader.qml b/tools/qmltime/tests/loader/component_loader.qml
index 8b338f37be..e8323e6bc0 100644
--- a/tools/qmltime/tests/loader/component_loader.qml
+++ b/tools/qmltime/tests/loader/component_loader.qml
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import QtQuick 2.0
import QmlTime 1.0 as QmlTime
diff --git a/tools/qmltime/tests/loader/empty_loader.qml b/tools/qmltime/tests/loader/empty_loader.qml
index 356195a4a9..2bdaa2d37f 100644
--- a/tools/qmltime/tests/loader/empty_loader.qml
+++ b/tools/qmltime/tests/loader/empty_loader.qml
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import QtQuick 2.0
import QmlTime 1.0 as QmlTime
diff --git a/tools/qmltime/tests/loader/no_loader.qml b/tools/qmltime/tests/loader/no_loader.qml
index 4de2dc26f1..3c3a562a5b 100644
--- a/tools/qmltime/tests/loader/no_loader.qml
+++ b/tools/qmltime/tests/loader/no_loader.qml
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import QtQuick 2.0
import QmlTime 1.0 as QmlTime
diff --git a/tools/qmltime/tests/loader/source_loader.qml b/tools/qmltime/tests/loader/source_loader.qml
index 38b47a134d..e6b8991d1b 100644
--- a/tools/qmltime/tests/loader/source_loader.qml
+++ b/tools/qmltime/tests/loader/source_loader.qml
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import QtQuick 2.0
import QmlTime 1.0 as QmlTime
diff --git a/tools/qmltime/tests/positioner_creation/no_positioner.qml b/tools/qmltime/tests/positioner_creation/no_positioner.qml
index c473a89d38..3f81500908 100644
--- a/tools/qmltime/tests/positioner_creation/no_positioner.qml
+++ b/tools/qmltime/tests/positioner_creation/no_positioner.qml
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import QtQuick 2.0
import QmlTime 1.0 as QmlTime
diff --git a/tools/qmltime/tests/positioner_creation/null_positioner.qml b/tools/qmltime/tests/positioner_creation/null_positioner.qml
index e459c4060f..4741b62ebf 100644
--- a/tools/qmltime/tests/positioner_creation/null_positioner.qml
+++ b/tools/qmltime/tests/positioner_creation/null_positioner.qml
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import QtQuick 2.0
import QmlTime 1.0 as QmlTime
diff --git a/tools/qmltime/tests/positioner_creation/positioner.qml b/tools/qmltime/tests/positioner_creation/positioner.qml
index 569dc8187c..e62aa5f72b 100644
--- a/tools/qmltime/tests/positioner_creation/positioner.qml
+++ b/tools/qmltime/tests/positioner_creation/positioner.qml
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import QtQuick 2.0
import QmlTime 1.0 as QmlTime
diff --git a/tools/qmltime/tests/vmemetaobject/null.qml b/tools/qmltime/tests/vmemetaobject/null.qml
index 520ad8bc0e..bb634824c5 100644
--- a/tools/qmltime/tests/vmemetaobject/null.qml
+++ b/tools/qmltime/tests/vmemetaobject/null.qml
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import QtQuick 2.0
import QmlTime 1.0 as QmlTime
diff --git a/tools/qmltime/tests/vmemetaobject/property.qml b/tools/qmltime/tests/vmemetaobject/property.qml
index df31145e5f..f217585f7f 100644
--- a/tools/qmltime/tests/vmemetaobject/property.qml
+++ b/tools/qmltime/tests/vmemetaobject/property.qml
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import QtQuick 2.0
import QmlTime 1.0 as QmlTime
diff --git a/tools/qmltime/textingrid.qml b/tools/qmltime/textingrid.qml
index 41073f50dc..8b2491fb26 100644
--- a/tools/qmltime/textingrid.qml
+++ b/tools/qmltime/textingrid.qml
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import QtQuick 2.0
import QmlTime 1.0 as QmlTime
diff --git a/tools/qmltyperegistrar/CMakeLists.txt b/tools/qmltyperegistrar/CMakeLists.txt
new file mode 100644
index 0000000000..74b1134e64
--- /dev/null
+++ b/tools/qmltyperegistrar/CMakeLists.txt
@@ -0,0 +1,28 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## qmltyperegistrar Tool:
+#####################################################################
+
+qt_get_tool_target_name(target_name qmltyperegistrar)
+qt_internal_add_tool(${target_name}
+ TARGET_DESCRIPTION "QML Types Registrar"
+ TOOLS_TARGET Qml # special case
+ INSTALL_DIR "${INSTALL_LIBEXECDIR}"
+ SOURCES
+ qmltyperegistrar.cpp
+ DEFINES
+ QT_NO_CAST_FROM_ASCII
+ QT_NO_CAST_TO_ASCII
+ LIBRARIES
+ Qt::Core
+ Qt::QmlTypeRegistrarPrivate
+)
+
+# support for .pro projects needing qmltyperegistrar
+set(qmltyperegistrar_mkspecs "${CMAKE_CURRENT_SOURCE_DIR}/qmltypes.prf")
+set(mkspecs_install_dir "${INSTALL_MKSPECSDIR}")
+qt_path_join(mkspecs_install_dir "${QT_INSTALL_DIR}" "${mkspecs_install_dir}" "features")
+qt_copy_or_install(FILES "${qmltyperegistrar_mkspecs}"
+ DESTINATION ${mkspecs_install_dir})
diff --git a/tools/qmltyperegistrar/qmltyperegistrar.cpp b/tools/qmltyperegistrar/qmltyperegistrar.cpp
new file mode 100644
index 0000000000..248926fb33
--- /dev/null
+++ b/tools/qmltyperegistrar/qmltyperegistrar.cpp
@@ -0,0 +1,207 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <QCoreApplication>
+#include <QCommandLineParser>
+#include <QDir>
+#include <QFile>
+#include <QScopedPointer>
+
+#include <cstdlib>
+
+#include <QtQmlTypeRegistrar/private/qqmltyperegistrar_p.h>
+#include <QtQmlTypeRegistrar/private/qqmltyperegistrarutils_p.h>
+
+using namespace Qt::Literals;
+
+int main(int argc, char **argv)
+{
+ // Produce reliably the same output for the same input by disabling QHash's random seeding.
+ QHashSeed::setDeterministicGlobalSeed();
+
+ // No, you are not supposed to mess with the message pattern.
+ // Qt Creator wants to read those messages as-is and we want the convenience
+ // of QDebug to print them.
+ qputenv("QT_MESSAGE_PATTERN", "%{if-category}%{category}: %{endif}%{message}");
+
+ QCoreApplication app(argc, argv);
+ QCoreApplication::setApplicationName(QStringLiteral("qmltyperegistrar"));
+ QCoreApplication::setApplicationVersion(QLatin1String(QT_VERSION_STR));
+
+ QCommandLineParser parser;
+ parser.addHelpOption();
+ parser.addVersionOption();
+
+ QCommandLineOption outputOption(QStringLiteral("o"));
+ outputOption.setDescription(QStringLiteral("Write output to specified file."));
+ outputOption.setValueName(QStringLiteral("file"));
+ outputOption.setFlags(QCommandLineOption::ShortOptionStyle);
+ parser.addOption(outputOption);
+
+ QCommandLineOption privateIncludesOption(
+ QStringLiteral("private-includes"),
+ QStringLiteral("Include headers ending in \"_p.h\" using \"#include <private/foo_p.h>\""
+ "rather than \"#include <foo_p.h>\"."));
+ parser.addOption(privateIncludesOption);
+
+ QCommandLineOption importNameOption(QStringLiteral("import-name"));
+ importNameOption.setDescription(QStringLiteral("Name of the module to use for type and module "
+ "registrations."));
+ importNameOption.setValueName(QStringLiteral("module name"));
+ parser.addOption(importNameOption);
+
+ QCommandLineOption pastMajorVersionOption(QStringLiteral("past-major-version"));
+ pastMajorVersionOption.setDescription(
+ QStringLiteral("Past major version to use for type and module "
+ "registrations."));
+ pastMajorVersionOption.setValueName(QStringLiteral("past major version"));
+ parser.addOption(pastMajorVersionOption);
+
+ QCommandLineOption majorVersionOption(QStringLiteral("major-version"));
+ majorVersionOption.setDescription(QStringLiteral("Major version to use for type and module "
+ "registrations."));
+ majorVersionOption.setValueName(QStringLiteral("major version"));
+ parser.addOption(majorVersionOption);
+
+ QCommandLineOption minorVersionOption(QStringLiteral("minor-version"));
+ minorVersionOption.setDescription(QStringLiteral("Minor version to use for module "
+ "registration."));
+ minorVersionOption.setValueName(QStringLiteral("minor version"));
+ parser.addOption(minorVersionOption);
+
+ QCommandLineOption namespaceOption(QStringLiteral("namespace"));
+ namespaceOption.setDescription(QStringLiteral("Generate type registration functions "
+ "into a C++ namespace."));
+ namespaceOption.setValueName(QStringLiteral("namespace"));
+ parser.addOption(namespaceOption);
+
+ QCommandLineOption pluginTypesOption(QStringLiteral("generate-qmltypes"));
+ pluginTypesOption.setDescription(QStringLiteral("Generate qmltypes into specified file."));
+ pluginTypesOption.setValueName(QStringLiteral("qmltypes file"));
+ parser.addOption(pluginTypesOption);
+
+ QCommandLineOption foreignTypesOption(QStringLiteral("foreign-types"));
+ foreignTypesOption.setDescription(
+ QStringLiteral("Comma separated list of other modules' metatypes files "
+ "to consult for foreign types when generating "
+ "qmltypes file."));
+ foreignTypesOption.setValueName(QStringLiteral("foreign types"));
+ parser.addOption(foreignTypesOption);
+
+ QCommandLineOption followForeignVersioningOption(QStringLiteral("follow-foreign-versioning"));
+ followForeignVersioningOption.setDescription(
+ QStringLiteral("If this option is set the versioning scheme of foreign base classes "
+ "will be respected instead of ignored. Mostly useful for modules who "
+ "want to follow Qt's versioning scheme."));
+ parser.addOption(followForeignVersioningOption);
+
+ QCommandLineOption jsroot(QStringLiteral("jsroot"));
+ jsroot.setDescription(
+ QStringLiteral("Use the JavaScript root object's meta types as sole input and do not "
+ "generate any C++ output. Only useful in combination with "
+ "--generate-qmltypes"));
+ parser.addOption(jsroot);
+
+ QCommandLineOption extract(u"extract"_s);
+ extract.setDescription(
+ u"Extract QML types from a module and use QML_FOREIGN to register them"_s);
+ parser.addOption(extract);
+
+ parser.addPositionalArgument(QStringLiteral("[MOC generated json file]"),
+ QStringLiteral("MOC generated json output."));
+
+ QStringList arguments;
+ if (!QmlTypeRegistrar::argumentsFromCommandLineAndFile(arguments, app.arguments()))
+ return EXIT_FAILURE;
+
+ parser.process(arguments);
+
+ const QString module = parser.value(importNameOption);
+
+ const QLatin1String jsrootMetaTypes
+ = QLatin1String(":/qt-project.org/meta_types/jsroot_metatypes.json");
+ QStringList files = parser.positionalArguments();
+ if (parser.isSet(jsroot)) {
+ if (parser.isSet(extract)) {
+ error(module) << "If --jsroot is passed, no type registrations can be extracted.";
+ return EXIT_FAILURE;
+ }
+ if (parser.isSet(outputOption)) {
+ error(module) << "If --jsroot is passed, no C++ output can be generated.";
+ return EXIT_FAILURE;
+ }
+ if (!files.isEmpty() || parser.isSet(foreignTypesOption)) {
+ error(module) << "If --jsroot is passed, no further metatypes can be processed.";
+ return EXIT_FAILURE;
+ }
+
+ files.append(jsrootMetaTypes);
+ }
+
+ MetaTypesJsonProcessor processor(parser.isSet(privateIncludesOption));
+ if (!processor.processTypes(files))
+ return EXIT_FAILURE;
+
+ processor.postProcessTypes();
+
+ if (!parser.isSet(jsroot)) {
+ processor.processForeignTypes(jsrootMetaTypes);
+ if (parser.isSet(foreignTypesOption))
+ processor.processForeignTypes(parser.value(foreignTypesOption).split(QLatin1Char(',')));
+ }
+
+ processor.postProcessForeignTypes();
+
+ if (parser.isSet(extract)) {
+ if (!parser.isSet(outputOption)) {
+ error(module) << "The output file name must be provided";
+ return EXIT_FAILURE;
+ }
+ QString baseName = parser.value(outputOption);
+ return QmlTypeRegistrar::runExtract(baseName, processor);
+ }
+
+ QmlTypeRegistrar typeRegistrar;
+ typeRegistrar.setIncludes(processor.includes());
+ typeRegistrar.setModuleNameAndNamespace(module, parser.value(namespaceOption));
+ QTypeRevision moduleVersion = QTypeRevision::fromVersion(
+ parser.value(majorVersionOption).toInt(), parser.value(minorVersionOption).toInt());
+ QList<quint8> pastMajorVersions;
+ for (const auto &x : parser.values(pastMajorVersionOption))
+ pastMajorVersions.append(x.toUInt());
+
+ typeRegistrar.setModuleVersions(moduleVersion, pastMajorVersions,
+ parser.isSet(followForeignVersioningOption));
+ typeRegistrar.setTypes(processor.types(), processor.foreignTypes());
+
+ if (!parser.isSet(jsroot)) {
+ if (module.isEmpty()) {
+ warning(module) << "The module name is empty. Cannot generate C++ code";
+ } else if (parser.isSet(outputOption)) {
+ // extract does its own file handling
+ QString outputName = parser.value(outputOption);
+ QFile file(outputName);
+ if (!file.open(QIODeviceBase::WriteOnly)) {
+ error(QDir::toNativeSeparators(outputName))
+ << "Cannot open file for writing:" << file.errorString();
+ return EXIT_FAILURE;
+ }
+ QTextStream output(&file);
+ typeRegistrar.write(output, outputName);
+ } else {
+ QTextStream output(stdout);
+ typeRegistrar.write(output, "stdout");
+ }
+ }
+
+ if (!parser.isSet(pluginTypesOption))
+ return EXIT_SUCCESS;
+
+ typeRegistrar.setReferencedTypes(processor.referencedTypes());
+ const QString qmltypes = parser.value(pluginTypesOption);
+ if (!typeRegistrar.generatePluginTypes(qmltypes)) {
+ error(qmltypes) << "Cannot generate qmltypes file";
+ return EXIT_FAILURE;
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/tools/qmltyperegistrar/qmltypes.prf b/tools/qmltyperegistrar/qmltypes.prf
new file mode 100644
index 0000000000..8011a758fb
--- /dev/null
+++ b/tools/qmltyperegistrar/qmltypes.prf
@@ -0,0 +1,137 @@
+CONFIG += metatypes
+
+qtPrepareLibExecTool(QML_TYPEREGISTRAR, qmltyperegistrar)
+
+isEmpty(QML_IMPORT_VERSION): \
+ QML_IMPORT_VERSION = $$IMPORT_VERSION
+
+# from moc.prf
+isEmpty(QML_IMPORT_MAJOR_VERSION):!isEmpty(QML_IMPORT_VERSION): \
+ QML_IMPORT_MAJOR_VERSION = $$section(QML_IMPORT_VERSION, ., 0, 0)
+
+isEmpty(QML_IMPORT_MINOR_VERSION):!isEmpty(QML_IMPORT_VERSION): \
+ QML_IMPORT_MINOR_VERSION = $$section(QML_IMPORT_VERSION, ., 1, 1)
+
+isEmpty(QML_IMPORT_NAME):!isEmpty(TARGETPATH) {
+ QML_IMPORT_NAME = $$replace(TARGETPATH, "/", ".")
+ QML_IMPORT_NAME = $$replace(QML_IMPORT_NAME, .$${QML_IMPORT_MAJOR_VERSION}$, '')
+}
+
+isEmpty(QML_IMPORT_NAME) {
+ error("Need TARGET_PATH or QML_IMPORT_NAME in order to generate qml types.");
+}
+
+isEmpty(QML_IMPORT_MAJOR_VERSION) {
+ error("Need IMPORT_VERSION, QML_IMPORT_VERSION, or QML_IMPORT_MAJOR_VERSION in order to generate qml types.");
+}
+
+isEmpty(QML_IMPORT_MINOR_VERSION) {
+ QML_IMPORT_MINOR_VERSION = 0
+}
+
+TARGET_BASENAME = $$lower($$basename(TARGET))
+TARGET_BASENAME ~= s/\s/_/g
+
+isEmpty(QMLTYPES_FILENAME) {
+ plugin: QMLTYPES_FILENAME = plugins.qmltypes
+ else: QMLTYPES_FILENAME = $${TARGET_BASENAME}.qmltypes
+}
+
+qt_module_deps = $$replace(QT, -private$, '')
+qt_module_deps += $$replace(QT_PRIVATE, -private$, '')
+qt_module_deps = $$resolve_depends(qt_module_deps, "QT.", ".depends" ".run_depends")
+qt_module_deps = $$replace(qt_module_deps, _private$, '')
+qt_module_deps = $$unique(qt_module_deps)
+
+# We know we need to prefer the CMake debug build type for qmake debug builds. Which of the CMake
+# release build types should be preferred for a qmake release build is guesswork. We apply a
+# heuristic here:
+#
+# If you've gone to the trouble of building a "minsizerel" Qt, you probably want to use it for your
+# qmake "release" builds. Conversely, if you have both a "release" and a "relwithdebinfo" Qt, you
+# probably want to use the "release" Qt build for qmake "release" builds and the "relwithdebinfo"
+# one for qmake "debug" builds.
+#
+# If no fitting build type exists, we accept the others. On linux, for example, we typically
+# have only a single "relwithdebinfo" Qt build for both debug and release builds of user projects.
+build_types = minsizerel release relwithdebinfo debug
+CONFIG(debug, debug|release): build_types = $$reverse(build_types)
+
+for(dep, qt_module_deps) {
+ android:ABI = _$${ANDROID_TARGET_ARCH}
+ infixed_module_name = $$eval(QT.$${dep}.module)
+
+ for(build_type, build_types) {
+ isEmpty(QT_LIBINFIX) {
+ metatypes_filename = $$lower($${infixed_module_name})$${ABI}_$${build_type}_metatypes.json
+ } else {
+ metatypes_filename = $$lower($$replace($${infixed_module_name}, $$QT_LIBINFIX, ''))$${ABI}_$${build_type}_metatypes.json
+ }
+ metatypes_filepath = $$[QT_INSTALL_ARCHDATA]/metatypes/$${metatypes_filename}
+ exists($${metatypes_filepath}) {
+ QML_FOREIGN_METATYPES += $${metatypes_filepath}
+ break()
+ }
+ }
+}
+
+
+QML_TYPEREGISTRAR_FLAGS = \
+ --generate-qmltypes=$$QMLTYPES_FILENAME \
+ --import-name=$$QML_IMPORT_NAME \
+ --major-version=$$QML_IMPORT_MAJOR_VERSION \
+ --minor-version=$$QML_IMPORT_MINOR_VERSION \
+ --foreign-types=$$join(QML_FOREIGN_METATYPES, ',')
+
+!isEmpty(QML_PAST_MAJOR_VERSIONS) {
+ for(past_major_version,QML_PAST_MAJOR_VERSIONS): QML_TYPEREGISTRAR_FLAGS += --past-major-version $$past_major_version
+}
+
+!isEmpty(MODULE_PRIVATE_INCLUDES): QML_TYPEREGISTRAR_FLAGS += --private-includes
+
+METATYPES_JSON = $${TARGET_BASENAME}_metatypes.json
+
+TYPEREGISTRATIONS = $${TARGET_BASENAME}_qmltyperegistrations$${first(QMAKE_EXT_CPP)}
+
+qmltyperegistrar_compiler.CONFIG += combine
+qmltyperegistrar_compiler.commands = \
+ $$QML_TYPEREGISTRAR $$QML_TYPEREGISTRAR_FLAGS -o ${QMAKE_FILE_OUT} ${QMAKE_FILE_IN}
+qmltyperegistrar_compiler.input = METATYPES_JSON
+qmltyperegistrar_compiler.output = $$TYPEREGISTRATIONS
+qmltyperegistrar_compiler.variable_out = SOURCES
+qmltyperegistrar_compiler.name = Automatic QML type registration
+qmltyperegistrar_compiler.dependency_type = TYPE_C
+QMAKE_EXTRA_COMPILERS += qmltyperegistrar_compiler
+
+!contains(TEMPLATE, "vc.*") { # work around QTBUG-91033
+ # Create a fake extra compiler to announce that we generate $$QMLTYPES_FILENAME.
+ # This allows us to use $$QMLTYPES_FILENAME as input in other extra compilers.
+ qmltyperegistrar_qmltypes.input = METATYPES_JSON
+ qmltyperegistrar_qmltypes.depends = $$TYPEREGISTRATIONS
+ qmltyperegistrar_qmltypes.output = $$QMLTYPES_FILENAME
+ qmltyperegistrar_qmltypes.CONFIG = no_link
+ qmltyperegistrar_qmltypes.commands = $$escape_expand(\\n) # force creation of rule
+ QMAKE_EXTRA_COMPILERS += qmltyperegistrar_qmltypes
+}
+
+install_qmltypes {
+ INSTALL_QML_FILES = false
+
+ android {
+ build_pass {
+ isEmpty(ANDROID_ABIS): ANDROID_ABIS = $$ALL_ANDROID_ABIS
+ ABI = $$first(ANDROID_ABIS)
+ equals(ABI, $$QT_ARCH): INSTALL_QML_FILES = true
+ }
+ } else: !debug_and_release|!build_all|CONFIG(release, debug|release): INSTALL_QML_FILES = true
+
+ equals(INSTALL_QML_FILES, true) {
+ isEmpty(QMLTYPES_INSTALL_DIR): \
+ QMLTYPES_INSTALL_DIR = $$[QT_INSTALL_QML]/$$TARGETPATH
+ do_install_qmltypes.files = $$OUT_PWD/$$QMLTYPES_FILENAME
+ do_install_qmltypes.path = $$QMLTYPES_INSTALL_DIR
+ do_install_qmltypes.CONFIG += no_check_exist
+ prefix_build: INSTALLS += do_install_qmltypes
+ else: COPIES += do_install_qmltypes
+ }
+}
diff --git a/tools/svgtoqml/CMakeLists.txt b/tools/svgtoqml/CMakeLists.txt
new file mode 100644
index 0000000000..fd6c91e7ea
--- /dev/null
+++ b/tools/svgtoqml/CMakeLists.txt
@@ -0,0 +1,32 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## svgtoqml Tool:
+#####################################################################
+
+qt_get_tool_target_name(target_name svgtoqml)
+qt_internal_add_tool(${target_name}
+ TARGET_DESCRIPTION "SVG to QML Converter"
+ TOOLS_TARGET Quick
+ SOURCES
+ main.cpp
+ LIBRARIES
+ Qt::Core
+ Qt::Gui
+ Qt::Qml
+ Qt::Quick
+ Qt::QuickVectorImageGeneratorPrivate
+)
+qt_internal_return_unless_building_tools()
+
+set(resource_files
+ "main.qml"
+)
+
+qt_internal_add_resource(${target_name} "qml"
+ PREFIX
+ "/"
+ FILES
+ ${resource_files}
+)
diff --git a/tools/svgtoqml/main.cpp b/tools/svgtoqml/main.cpp
new file mode 100644
index 0000000000..8ae290db84
--- /dev/null
+++ b/tools/svgtoqml/main.cpp
@@ -0,0 +1,113 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <QGuiApplication>
+#include <QQmlApplicationEngine>
+#include <QCommandLineParser>
+#include <QFile>
+#include <QQuickWindow>
+#include <QQuickItem>
+#include <QtQuickVectorImageGenerator/private/qquickitemgenerator_p.h>
+#include <QtQuickVectorImageGenerator/private/qquickqmlgenerator_p.h>
+#include <QtQuickVectorImageGenerator/private/qquickvectorimageglobal_p.h>
+
+#define ENABLE_GUI
+
+int main(int argc, char *argv[])
+{
+#ifdef ENABLE_GUI
+ QGuiApplication app(argc, argv);
+#else
+ QCoreApplication app(argc, argv);
+#endif
+
+ QCommandLineParser parser;
+ parser.setApplicationDescription("SVG to QML converter [tech preview]");
+ parser.addHelpOption();
+ parser.addPositionalArgument("input", QCoreApplication::translate("main", "SVG file to read."));
+ parser.addPositionalArgument("output", QCoreApplication::translate("main", "QML file to write."));
+
+ QCommandLineOption optimizeOption("optimize-paths",
+ QCoreApplication::translate("main", "Optimize paths for the curve renderer."));
+ parser.addOption(optimizeOption);
+
+ QCommandLineOption curveRendererOption("curve-renderer",
+ QCoreApplication::translate("main", "Use the curve renderer in generated QML."));
+ parser.addOption(curveRendererOption);
+
+ QCommandLineOption typeNameOption(QStringList() << "t" << "type-name",
+ QCoreApplication::translate("main", "Use <typename> for Shape."),
+ QCoreApplication::translate("main", "typename"));
+ parser.addOption(typeNameOption);
+
+ QCommandLineOption copyrightOption("copyright-statement",
+ QCoreApplication::translate("main", "Add <string> as a comment at the start of the generated file."),
+ QCoreApplication::translate("main", "string"));
+ parser.addOption(copyrightOption);
+
+ QCommandLineOption outlineModeOption("outline-stroke-mode",
+ QCoreApplication::translate("main", "Stroke the outside of the filled shape instead of "
+ "the original path. Also sets optimize-paths."));
+ parser.addOption(outlineModeOption);
+
+#ifdef ENABLE_GUI
+ QCommandLineOption guiOption(QStringList() << "v" << "view",
+ QCoreApplication::translate("main", "Display the SVG in a window."));
+ parser.addOption(guiOption);
+#endif
+ parser.process(app);
+ const QStringList args = parser.positionalArguments();
+ if (args.size() < 1) {
+ parser.showHelp(1);
+ }
+
+ const QString inFileName = args.at(0);
+
+ QString commentString = QLatin1String("Generated from SVG file %1").arg(inFileName);
+
+ const auto outFileName = args.size() > 1 ? args.at(1) : QString{};
+ const auto typeName = parser.value(typeNameOption);
+ auto copyrightString = parser.value(copyrightOption);
+
+ if (!copyrightString.isEmpty()) {
+ copyrightString = copyrightString.replace("\\n", "\n");
+ commentString = copyrightString + u"\n" + commentString;
+ }
+
+ QQuickVectorImageGenerator::GeneratorFlags flags;
+ if (parser.isSet(curveRendererOption))
+ flags |= QQuickVectorImageGenerator::GeneratorFlag::CurveRenderer;
+ if (parser.isSet(optimizeOption))
+ flags |= QQuickVectorImageGenerator::GeneratorFlag::OptimizePaths;
+ if (parser.isSet(outlineModeOption))
+ flags |= (QQuickVectorImageGenerator::GeneratorFlag::OutlineStrokeMode
+ | QQuickVectorImageGenerator::GeneratorFlag::OptimizePaths);
+
+ QQuickQmlGenerator generator(inFileName, flags, outFileName);
+ generator.setShapeTypeName(typeName);
+ generator.setCommentString(commentString);
+ generator.generate();
+
+#ifdef ENABLE_GUI
+ if (parser.isSet(guiOption)) {
+ app.setOrganizationName("QtProject");
+ const QUrl url(QStringLiteral("qrc:/main.qml"));
+ QQmlApplicationEngine engine;
+ QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
+ &app, [&](QObject *obj, const QUrl &objUrl){
+ if (!obj && url == objUrl)
+ QCoreApplication::exit(-1);
+ if (obj) {
+ auto *containerItem = obj->findChild<QQuickItem*>(QStringLiteral("svg_item"));
+ QQuickItemGenerator generator(inFileName, flags, containerItem);
+ generator.generate();
+ }
+ });
+ engine.load(url);
+ return app.exec();
+ }
+#else
+ return 0;
+#endif
+
+}
diff --git a/tools/svgtoqml/main.qml b/tools/svgtoqml/main.qml
new file mode 100644
index 0000000000..9a8ceefe5d
--- /dev/null
+++ b/tools/svgtoqml/main.qml
@@ -0,0 +1,21 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import QtQuick
+import QtQuick.Shapes
+import QtQuick.Controls
+import QtQuick.Layouts
+
+Window {
+ id: mainWindow
+ width: 1280
+ height: 960
+ visible: true
+ color: "white"
+
+ Item {
+ id: svg
+ objectName: "svg_item"
+ anchors.centerIn: parent
+ }
+}